Funkcje versus wyrażenia

Różne sposoby definiowania funkcji

Matematycy definiują funkcję ze zbioru A do zbioru B jako operację przyporządkowującą jednoznacznie każdemu elementowi zbioru A pewien element zbioru B. Zbiorami tymi są najczęściej liczby rzeczywiste, zespolone lub naturalne. W tym ostatnim przypadku funkcję nazywamy ciągiem.

Funkcję definiujemy określając jednoznacznie co ma ona robić. Jeżeli ilość elementów zbioru A jest skończona, to funkcję można określić po prostu wypisując przyporządkowane elementy. Na ogół jednak funkcja jest określona na zbiorze o nieskończonej liczebności (np: liczby rzeczywiste na odcinku od zera do 1), i funkcję musimy zadać w inny sposób. W znakomitej większości przypadków jest to opis przy użyciu jednego lub kilku wzorów matematycznych. Dlatego bardzo często w szkolnej matematyce utożsamia się w praktyce pojęcie funkcji i definiujacego ją wzoru (wyrażenia). Ułatwia to z jednej strony rachunki ręczne, ale z drugiej strony znacznie utrudnia zrozumienie pewnych operacji, np: znajdywania funkcji odwrotnej czy składania funkcji. Mathematica rozróżnia te dwa pojęcia, co początkowo sprawia trudność (np: przy różniczkowaniu mamy osobne polecenia do różniczkowania wyrażeń i funkcji: D i Derivative, rozwiązaniem równania różniczkowego może być funkcja lub jej wzór, zależnie od wywołania DSolve, itp.), ale potem procentuje poprzez znacznie lepsze zrozumienie analizy matematycznej (przykłady: pochodna funkcji odwrotnej, zamiana zmiennych w równaniach różniczkowych).

Sposób 1 (Przykład1: zdefiniuj funkcję obliczającą kwadrat liczby; Przykład 2: zdefiniuj funkcję rozwiązującą numerycznie równanie (x^(x^x))=a w zależności od parametru a)

Podstawowy sposób definiowania funkcji zademostrujemy na funkcji której zadaniem będzie podnoszenie liczby do kwadratu. Nazwiemy ją sqr z dwóch powodów:
a) w wielu starszych językach programowania występuje funkcja o takiej nazwie i funkcji
b) jest to jedna z niewielu często uzywanych funkcji, która nie została zdefinowana przez programistów WolframResearch (zapewne po to, aby mozna było jej użyć w Help-ie jako przykład...)

sqr[x_]:=x^2 (* funkcja podnosząca do kwadratu *)

sqr[2]

sqr[x]

sqr[(a+b)]

sqr[a+b]

sqr[x_]:=x^2 (* funkcja podnosząca do kwadratu *);

Definicja funkcji składa się z następujących elementów:
sqr - nazwa funkcji (obowiązkowa); zaleca się aby własne funkcje posiadały nazwy zaczynające się z małej litery, co gwarantuje uniknięcie kolizji oznaczeń  z wbudowanymi funkcjami (które zawsze zaczynają się z wielkiej litery)
[] - nawiasy kwadratowe, wewnątrz których jest miejsce na podanie argumentów funkcji
x_ - argument funkcji (w przykładzie literka x, ale może to być cokolwiek zgodnego ze składnią) zakończony znakiem podkreślenia (służy on do sprawdzenia czy podany x pasuje do zadanego wzorca, sam podkreślnik oznacza, że funkcja przyjmuje dowolne argumenty)
:=  - symbol ,,opóźnionej definicji’’ funkcji oznacza, że PRAWA strona ma być obliczona dopiero w momencie wywołania funkcji
x^2 - wzór funkcji (x do potęgi 2)

Tak zdefiniowana funkcja, usiłuje wykonać zadaną operację nawet gdy nie ma to żadnego sensu:

sqr[Graphics[Circle[{0,0},1/5]]]

sqr[False]

W Mathematice  to na użytkowniku spoczywa ciężar pilnowania, czy sposób użycia funkcji ma sens.

Próba zdefiniowania funkcji, która już istnieje (jako wbudowana), powoduje błąd:

Sqrt[x_]:=x^(1/2)

Użycie nazwy z małej litery pozwala nam na zdefiniowanie pierwiastka wg. własnego uznania:

sqrt[x_]:=Sqrt[Abs[x]]

sqrt[-1]

Sqrt[-1]

sqrt2[x_]:=(((1+3 x)/(3+x))) (* przybliżony pierwiastek dla x≈1 *)

Plot[{Sqrt[x],sqrt2[x]},{x,0,4}]

Typowo, prawą stroną definicji funkcji jest jakiś wzór matematyczny. Może to być także dowolne sensowne wyrażenie programu Mathematica .
Rozważmy np: funkcję która obliczy rozwiązanie równania x^x^x=a, w zależności od parametru a. Jest to równanie nierozwiązywalne symbolicznie:

FindInstance[x^x^x==2,x,Reals]

Ale bez problemu rozwiązywalne numerycznie, dla pewnego ustalonego a, np: a=2

FindRoot[x^x^x==2,{x,1}]

Teraz musimy ,,wydobyć’’ wartość wg. wzorcowego schematu:

x/.FindRoot[x^x^x==3,{x,1}]

Następnie, wstawiamy powyższe wyrażenie do definicji funkcji (ang: body), nie zapominając o zamianie liczby 3 na parametr a:

rozw[a_]:=(x/.FindRoot[x^x^x==a,{x,1}])

Teraz możemy obliczyć wartość funkcji:

rozw[3]

Lub narysować jej wykres:

Plot[rozw[p],{p,0,4}]

Obliczenie pochodnej  wymaga innego sposobu zdefiniowania takiej funkcji, np: funkcji interpolującej (InterpolatingFunction)

D[rozw[p],p]

Całkę oznaczoną obliczymy, aczkolwiek wyprodukowane zostanie sporo błędów:

NIntegrate[rozw[t],{t,0,2}]

Poniżej sposób korekty tego błędu; zażądamy, aby funkcja była obliczana wylącznie numerycznie:

Clear[rozw2]

rozw2[a_?NumberQ]:=(x/.FindRoot[x^x^x==a,{x,1}])

rozw2[1]

rozw2[Pi]

rozw2[x]

NIntegrate[rozw2[t],{t,0,Pi}]

rozw3[a_?NumericQ]:=(x/.FindRoot[x^x^x==a,{x,1}])

rozw3[1]

rozw3[Pi]

rozw3[GoldenRatio]

rozw3[p]

Sposób 2: użycie polecenia Function

Drugi sposób określenia funkcji można  określić jako ,,nazwanie operacji matematycznej’’. Definicja jest następująca:

kwadrat=Function[x,x^2]

Poniżej wyjaśniam sens poszczególnych składników:
kwadrat - nazwa funkcji (poprzednio nazwalismy ją sqr, ale to TA SAMA FUNKCJA!)
= - znak pojedynczej równości, czyli przypisania; funkcję (operację) po prawej stronie zapisaliśmy pod nazwą kwadrat
Function - polecenie programu Mathematica definiujące funkcję, posiada ono dwa argumenty (oczywiście w pojedynczych nawiasach kwadratowych):
x - pierwszy argument, nazwa zmiennej na której wykonamy operacje ,,matematyczne’’ (WEJŚCIE)
x^2 - drugi argument, operacje które należy wykonać i równocześnie zwracany wynik działania funkcji (WYJŚCIE)

Tak zdefiniowana funkcja działa identycznie jak poprzednia:

kwadrat[4]

kwadrat[I]

kwadrat[a+b]

kwadrat[kwadrat[x]]

Obie definicje możemy ,,mieszać’’:

kwadrat[sqr[x]]

Funkcję można podstawić do wzoru:

f[t]+f[t]^2/.f->Function[x,x^2]

f[t]/.f->sqr

?f

?sqr

?kwadrat

sqr'

kwadrat'

Warto zwrócić uwagę na fakt, iż symbol x użyty wewnątrz definicji funkcji, nie ma znaczenia. Równie dobrze możemy napisać:

kwadratA=Function[iks,iks*iks]

Na koniec drugi przykład (rozwiązanie x^x^x=a).  Skomplikowane definicje oznaczają więcej możliwości popełnienia błędów. Poniżej przykład fatalnej pomyłki wynikającej z notorycznego nadużywania symbolu x, oznaczającego w notacji ,,tablicowej’’ wszystko, zaczynając od argumentów funkcji, poprzez niewiadome w równaniach, na zmiennych całkowania i granicach całek oznaczonych kończąc. Pomyłki te są w Mathematice  o tyle uciążliwe, że ujawniają się dopiero przy wywołaniu funkcji, które często następuje po raz pierwszy wewnątrz jeszcze innej funkcji, typowo Plot.

rozw2=Function[x,x/.FindRoot[x^x^x==a,{x,1}]]

rozw2[1]

Poprawny sposób:

rozw2=Function[a,x/.FindRoot[x^x^x==a,{x,1}]]

rozw2[3]

Sposób 3: funkcja czysta (szkieletowa) (ang: pure function)

Czysta funkcja jest formalnie identyczna z funkcją definiowaną poleceniem Function, ale sposób jej zapisu jest na tyle odmienny i egzotyczny dla początkujących, że zasługuje na osobne omówienie. Bez zrozumienia notacji czystych funkcji (inaczej szkieletowych), większość trudniejszych przykładów z Help-a może okazać się niezrozumiała. Przejdźmy do konkretów. Funkcja podnosząca do kwadratu to:

kw=#^2&

Na początek sprawdzenie, że powyższy ,,konstrukt’’ rzeczywiście działa:

kw[2]

kw[a+b]

kw[Sqrt[x]]

kw[x^2]

kw=#^2&;

Z powyższą notacją zetkneliśmy się np: przy okazji omawiania symbolu Root. Rozbierzmy definicję funkcji kw na czynniki:
kw - nazwa funkcji
= przypisanie nazwy kw definicji po prawej stronie
# - ,,hasz’’ (na klawiaturze Shift+3, często #1, polecenie Slot) - de facto jest to odpowiednik symbolu x w szkolnych definicjach funkcji. Wybrano dla niego tak egzotyczne oznaczenie, aby podkreślić fakt, iż funkcja jest obiektem istniejącym niezależnie od ,,literek’’ użytych przy zapisie jej definicji
#^2 - definicja funkcji: ,,coś’’ podniesione do kwadratu
& - znak zarezerwowany na oznaczenie końca definicji funkcji

Funkcje w powyższej postaci są najbardziej użyteczne, gdy nie ma sensu nadawać im nazwy. Powiedzmy, że chcemy zastosować funkcję x+1/x do listy {a,b,c,d,e}. Nie znając czystych funkcji postapilibyśmy  np. tak:

f[x_]:=x+1/x

f[{a,b,c,d,e}]

Jeżeli jest to jednorazowa operacja, to bardziej sensowne jest użycie funkcji szkieletowych:

(#+1/#&)[{a,b,c}]

Mam nadzieję, że teraz zrozumiała będzie definicja drugiej przykladowej funkcji:

sol=x/.FindRoot[x^x^x==#,{x,1}]&

sol[4]

Z czasem można nabrać wprawy w pisaniu i odczytywaniu konstrukcji składniowych jak wyżej. Jest to niezbędna wiedza, głównie ze względu na popularność tej notacji wśród użytkowników programu Mathematica. Jest to jedna z umiejętności, która jest wyznacznikiem należenia do ,,klubu’’ zaawansowanych użytkowników. Drugą jest umiejętność posługiwania się wzorcami (ang. pattern).

Sposób 4: funkcje pamiętające

Osoby, które z różnych powodów muszą powalczyć z przyspieszeniem swoich programów, muszą znać opisaną niżej technikę definiowania funkcji. Każdy wcześniej czy później trafi na barierę czasu obliczeniowego. Jednym ze sposobów na usprawnienie rachunków jest mechanizm zapamiętywania raz obliczonych wartości funkcji. Kolejne wywołania dla tego samego argumentu polegają na odczytaniu gotowego wyniku z pamięci komputera, co w większości przypadków oznacza szybszy rachunek. Typowo czas ten wynosi ZERO, czyli jest niemierzalnie mały. Nie ma nic za darmo: w ten sposób błyskawicznie zapychamy pamięć komputera.

Struktura definicji jest identyczna jak przy standardowej, ale dodany jest środkowy składnik ze znakiem pojednynczej równości:

Clear[kwadratPamietajacy]

kwadratPamietajacy[x_]:=kwadratPamietajacy[x]=x^2

Dopóki nie wywołamy funkcji, obie wersje wyglądają na identyczne:

?kwadratPamietajacy

?kwadrat

Obliczmy wartość funkcji dla kilku x-ów:

Table[kwadrat[x],{x,0,10}]

Definicja funkcji nie zmieniła się:

?kwadrat

Table[kwadratPamietajacy[x],{x,0,10}]

?kwadratPamietajacy

Widać powyżej, że wyniki dla x=0,1,2,3,4,5,6,7,8,9,10 zostały zapamiętane!

Funkcja kwadrat jest zbyt prosta, aby zaobserwować przyspieszenie obliczeń. Spróbujmy z drugim przykładem.

sol[2]

Clear[solP]

solP[a_]:=solP[a]=x/.FindRoot[x^x^x==a,{x,1}]

Czas rozwiązania 2000 równań to około 0.5 sekundy:

Table[sol[x],{x,0,2,0.001}];//Timing

Za drugim razem podobnie:

Table[sol[x],{x,0,2,0.001}];//Timing

Sprawdzamy dla f. pamietającej:

Table[solP[x],{x,0,2,1/1000}];//Timing

Natomiast za drugim razem czas obliczeń to prawie ZERO:

Table[solP[x],{x,0,2,1/1000}];//Timing

(* ?solP *)

Co ciekawae, i nieco zaskakujące, mechanizm zapamiętywania często działa także dla liczb zmiennoprzecinkowych:

Table[solP[x],{x,0,2,0.001}];//Timing

Table[solP[x],{x,0,2,0.001}];//Timing

Table[solP[x],{x,Pi,2Pi,Sqrt[2.0]/1000.0}];//Timing

Table[solP[x],{x,Pi,2Pi,Sqrt[2.0]/1000.0}];//Timing

Należy podkreślić, iż zapamiętane wartości funkcji nie muszą wcale być liczbami. Mogą to być dowolne wyrażenia Mathematici np: całe rozwiązania numeryczne równań różniczkowych, macierze, wykresy itp. itd.

Sposób 5: funkcje interpolujace

Funkcje definiowane w Sposób 1-4 mają zastosowanie ograniczone typowo do obliczania wartości. Często chcielibyśmy mieć możliwość bardziej elastycznego operowania, np: możliwość różniczkowania. W innej sytuacji, mamy możliwość obliczenia funkcji tylko w małej liczbie punktów, np. z powodu braku mocy obliczeniowej. Lub po prostu funkcja jest zadana (znana) tylko dla kilku punktów. W tej sytuacji najwygodniejsze będzie utworzenie funkcji interpolującej. Wiele poleceń programu  Mathematica produkuje wyniki właśnie w takiej postaci, np: NDSolve.

Ponownie rozważmy funkcję podnoszącą do kwadratu. Zaczynamy od wygenerowania tabilcy jej wartości w pewnym przedziale:

tblKw=Table[{x,kw[x]},{x,0,8,1}]

Następnie poleceniem Interpolation tworzymy funkcję interpolującą przechodzącą przez zadane punkty. Dokładność takiej funkcji zależy od wielu czynników. W przypadku funkcji kwadratowej wynik jest ścisły!

kwInterp=Interpolation[tblKw]

kwInterp[2]

kwInterp[1/2]

O dziwo, nawet dla x=Pi dostajemy dokładny rezultat:

kwInterp[Pi]

kw[Pi]

kwInterp[Pi]//Simplify

Wykresy nie są w stanie ujawnić różnicy:

Plot[{kw[x],kwInterp[x],x^2},{x,0,8}]

Różnice po odjęciu są na poziomie $MachineEpsilon:

Plot[{kw[x]-kwInterp[x],$MachineEpsilon,-$MachineEpsilon},{x,0,8}]

Funkcję interpolującą możemy różniczkować tyle razy, ile wynosi rząd interpolacji (zwykle: 3); potem otrzymamy zero.

Plot[{2x,kwInterp'[x]},{x,0,8}]

Plot[{2-kwInterp''[x]},{x,0,8}]

Przykład 2:

Table[{x,rozw2[x]},{x,0,4,0.01}];

rozwInterp=Interpolation[%]

Plot[rozwInterp[x],{x,0,4}]

Na wykresie widać skutek błędu dla x=0. Poprawimy go ręcznie doklejając punkt {0,0} na początku tabeli wartości:

rozwInterp=Interpolation[Join[{{0,0}},Table[{x,rozw2[x]},{x,0.01,4,0.01}]]];

Plot[rozwInterp[x],{x,-1,5}]

Teraz możemy np: narysować drugą pochodną:

Plot[rozwInterp''[x],{x,0,4},PlotRange->All]

...i obliczyć położenie punktu przegiecia wykresu (miejsce zerowe 2 pochodnej):

FindRoot[rozwInterp''[x]==0,{x,0.8}]

Sprawdzimy jeszcze dokładność interpolacji:

Plot[{rozw2[x],rozwInterp[x]},{x,0,4}]

Plot[{rozw2[x]-rozwInterp[x]},{x,0,4}]

W dużym powiększeniu można sprawdzić, że już 3 pochodna jest funkcją ,,schodkową’’; czwarta będzie więc równa zero.

Plot[rozwInterp''''[x],{x,1,2}]

Równość funkcji?

Dochodzimy do bardzo delikatnej kwestii równości funkcji. Wiemy doskonale, że np:  sqr (Sposób 1) i kwadrat (Sposób 2) to ta sama funkcja, tylko pod inną nazwą, i zdefiniowana w nieco inny sposób. Niestety, programiści jak dotąd nie rozwiązali problemu równości funkcji, i nie wiadomo czy w ogóle takie pojęcie ma sens.

?sqr

kwadrat==sqr

kwadrat[x]

sqr[x]

kwadrat[x]==sqr[x]

kwadrat2=Function[iks,iks*iks]

kwadrat3==kwadrat

kwadrat3=Function[iks,iks^2]

kwadrat2==kwadrat2

Simplify[%]

FullSimplify[%]

Różnica pomiędzy funkcją a jej wzorem

Jaka jest różnica pomiędzy symbolami Sin a Sin[x] ? Większość odpowie, że żadna, bo jedno i drugie to funkcja sinus. Formalnie jednak, Sin to ,,czysta’’ funkcja, natomiast Sin[x] to jej wzór (wyrażenie matematyczne) po zastosowaniu funkcji sinus do argumentu x. Wygląda to na przerost formy nad treścią, ale rozważmy podobny problem dla ,,funkcji’’ 1/x. Czy symbol ,,1/x’’ to funkcja czy wyrażenie? Zawiera symbol x, a więc wyrażenie. Czym jest w tym przypadku ,,czysta’’ funkcja? Problem ujawnia się na dalszym etapie edukacji, np: przy obliczaniu szeregu Maclaurina:

Clear[f]

Series[f[x],{x,Pi,2}]//Normal

Do powyższego wzoru chcemy wstawić zamiast dowolnej funkcji f, pewną konkretną funkcję, powiedzmy sinus:

f[π]+(-π+x) f'[π]+(1/2) (((-π+x))^2) f''[π]/.f->Sin

A co z , , funkcją''  1/x:

f[π]+(-π+x) f'[π]+(1/2) (((-π+x))^2) f''[π]/.f->1/x

Dostaliśmy jakieś bzdury. Problem polega na tym, że zamiast f chcemy podstawić funkcję a nie jej wzór matematyczny! W jeszcze bardziej abstrakcyjnym podejściu, funkcja może nie posiadać żadnego wzoru. Co powinniśmy zrobić? Musimy zdefinować funkcję obliczającą ,,1/x’’.

odwrotnosc[iks_]:=1/iks

f[π]+(-π+x) f'[π]+(1/2) (((-π+x))^2) f''[π]/.f->odwrotnosc

Simplify[%]

f[π]+(-π+x) f'[π]+(1/2) (((-π+x))^2) f''[π]/.f->(1/#&)

Innym przykładem, gdzie powyższe rozróżnienie jest konieczne, to obliczanie funkcji odwrotnych:

InverseFunction[Exp]

InverseFunction[1/x]  (* brak wyniku, bo 1/x to nie jest funkcja! *)

InverseFunction[odwrotnosc] (* teraz jest poprawnie, funkcja 1/x jest odwrotna do samej siebie  *)

Bardzo przydatna w praktyce jest umiejetność zdefiniowania funkcji na podstawie jej wzoru, który bywa bardzo dlugi, i nie chcemy, lub nie jesteśmy w stanie skopiować go klawiszami Ctrl+C Ctrl+V.

wzor=Sum[1/k^3/(k^6+1),{k,1,n}]

ff=Function[n,(1/6) (-3 PolyGamma[2,1]+3 PolyGamma[2,1+n]-RootSum[1+((#1)^6)&,PolyGamma[0,1-#1] ((#1)^4)&]+RootSum[1+((#1)^6)&,PolyGamma[0,1+n-#1] ((#1)^4)&])];

ff[3]

N[%]

Poniżej błędny sposób:

fun=Function[n,wzor]

fun[12]

N[%]

Chop[%]

To jest poprawna definicja, z użyciem Evaluate:

fun2=Function[n,Evaluate[wzor]]

fun2[10]

N[%]

Chop[%]

fun3=Function[n,N[Evaluate[wzor]]] (* ŹLE ! *)

fun3[10]

fun4=Function[n,Evaluate[N[Evaluate[wzor]]]] (* DOBRZE! *)

fun4[10]

fun5=Function[n,Evaluate[N[Evaluate[wzor]]//Re]]

fun5[10]

Funkcje nazwane i funkcje ,,czyste’’ (szkieletowe)

Często negowana jest potrzeba stosowania funkcji szkieletowych, argumentując, że każdą potrzebną funkcję można sobie wcześniej zdefiniować, posługując się ,,normalną’’ notacją z użyciem zmiennej ,,x’’ lub podobnej. Sensownym przykładem, w ktorym wygodnie jest podstawiać funkcje do wzorów, jest sprawdzanie parzystości, nieparzystości i okresowości funkcji. Sprawdzamy, czy zachodzą poniższe tożsamości:

okresowa =f[x+2Pi]==f[x]

parzysta=f[x]==f[-x]

nieparzysta=f[-x]==-f[x]

Jeżeli funkcja posiada nazwę, to po prostu zamieniamy symbol f na symbol, np: Sin:

{okresowa,parzysta, nieparzysta}/.f->Sin

{okresowa,parzysta, nieparzysta}/.f->Cos

{okresowa,parzysta, nieparzysta}/.f->Exp

{okresowa,parzysta, nieparzysta}/.f->Sinh

Co w sytuacji gdy funkcja nie posiada żadnej nazwy zwyczajowej, np: funkcja stala, zdefiniowana wzorem 1/x, itp.
Możemy oczywiście nadać tym funkcją mniej lub bardziej sensowne nazwy:

jeden = Function[x,1]

odwrotnosc = Function[x,1/x]

tozsamosc=Function[x,x]

Do tej listy można dołożyć jeszcze podnoszenie do kwadratu, do n-tej potęgi itp:

{okresowa,parzysta, nieparzysta}/.f->jeden

{okresowa,parzysta, nieparzysta}/.f->odwrotnosc

{okresowa,parzysta, nieparzysta}/.f->tozsamosc

{okresowa,parzysta, nieparzysta}/.f->kwadrat

Problem polega na tym, że powyższe nazwy zwykle nic nie mówią nikomu, poza ich autorem. Używając funkcji szkieletowej osiągamy ten sam efekt, przy czym zapis pozostaje bardzo czytelny, o ile definicja nie jest zbyt skomplikowana:

{okresowa,parzysta, nieparzysta}/.f->(#^2&)

UWAGA: nawias otaczajacy ,,czystą funkcję’’ jest obowiązkowy!

{okresowa,parzysta, nieparzysta}/.f->#^2& (* to jest BŁĄD !!! *)

Używając powyższej postaci, bez problemu podstawiamy także funkcje wielu zmiennych:

{okresowa,parzysta, nieparzysta}/.f->(Power[#,2]&)

{okresowa,parzysta, nieparzysta}/.f->(Power[#,5]&)

{okresowa,parzysta, nieparzysta}/.f->(1&)

{okresowa,parzysta, nieparzysta}/.f->(0&)

{okresowa,parzysta, nieparzysta}/.f->(1/#&)

Generalnie, zalecam stosowanie takiej notacji dla prostych nienazwanych funkcji, za wyjątkiem sytuacji, w  której kod ma być odczytany przez osoby nieobeznane z taką notacją.

Różne sposoby wywoływania funkcji jednej zmiennej

Istnieją trzy podstawowe sposoby wywoływania funcji jednej zmiennej: nawiasowa (szkolna) , prefiksowa oraz postfiskowa. Najlepiej prześledzić to na przykładach:

kwadrat=Function[x,x^2]

kwadrat[2] (* notacja nawiasowa, najpierw nazwa funkcji, potem argument otoczony nawiasami kwadratowymi *)

kwadrat@2 (* notacja prefixowa, najpierw funkcja, potem argument *)

2//kwadrat  (* notacja postfiksowa, najpierw argument, potem funkcja *)

Ważny wyjątek to silnia, wywoływana nastepująco:

12!

Co nie zmienia faktu, że możemy użyć pełnej nazwy silni, czyli Factorial:

Factorial[12]

Factorial@12

12//Factorial

Hold[12!]

FullForm[%]

Podobnie możemy postępować z listami:

Sqrt@{1,2,3,4}

{1,2,3,4}//Sqrt//N

Sqrt[{1,2,3,4}]

Zwykle wywołanie fukcji jednej zmiennej dla listy, powoduje zastosowanie tejże funkcji do każdego z elementów z osobna.

Składanie funkcji

Dwukrotne i wielokrotne składanie funkcji

Składanie funkcji to wielokrotne jej obliczanie, przy czym wynik działania funkcji jest argumentem dla kolejnego jej wywołania. W przykładzie poniżej, stosujemy funkcję kwadrat, czyli podnosimy z do 2 potęgi (co daje x^2) a następnie wynik, czyli x^2 jeszcze raz podnosimy do kwadratu. Można to zapisać na wiele sposobów:

kwadrat@kwadrat[x]

kwadrat[kwadrat[x]]

x//kwadrat//kwadrat

Poszczególne wywołania można mieszać. Jest to szczególnie przydatne, gdy zależy nam na maksymalnej nieczytelności kodu:

kwadrat[x]//kwadrat

kwadrat[x//kwadrat]

kwadrat[kwadrat@x]

kwadrat@x//kwadrat

Wielokrotne składanie funkcji samej ze sobą:

kwadrat[kwadrat[kwadrat[kwadrat[x]]]]

Sposób zapisu uwypuklający rolę nawiasów:

kwadrat[
kwadrat[
kwadrat[
kwadrat[x]
]
]
]

Użycie notacji @ :

kwadrat@kwadrat@kwadrat@kwadrat@x

Notacja postfiksowa pozwala na łatwe dokładanie kolejnych złożeń, przez proste dopisanie na końcu:

x//kwadrat//kwadrat//kwadrat//kwadrat

Automatyczne składanie funkcji poleceniem Nest (Przykład: rozwiąż równanie typu f[x]=x, np: Cos[x]=x )

Składanie funkcji jest operacją o pierwszorzędnym znaczeniu w Mathematice. Wielokrotne składanie pozwala na elegancki zapis wielu algorytmów. Służy do tego polecenie Nest.

Nest[kwadrat,x,2]

Polecenie Nest posiada trzy argumenty
kwadrat - nazwa funkcji którę będziemy składać
x - argument pierwszej ze składanych funkcji (to od czego startujemy)
2 - ilość złożeń

Nest[f,x,5]

Nest[kwadrat,x,7]

Nest[kwadrat,2,4]

Istnieje kilka wariantów polecenia: 
NestList  - wyświetla listę wszystkich złożeń częściowych
NestWhile - składa funkcję do momentu w którym zostanie spełniony zadany warunek
FixedPoint - składa funkcję tak długo, aż wynik przestanie się zmieniać

NestList[kwadrat,x,6]

Składaj do momentu w którym wynik przekroczy 100:

NestWhile[kwadrat,2,#<100&]

NestList[kwadrat,0.5,12]

Manipulate[Nest[1+1/#&,x,n],{n,1,13,1}]

W czasch gdy używano kalkulatorów, uczniowie którzy nudzili się na lekcjach, naciskali różne klawisze na kalkulatorze. Mozna było zauważyć, że naciskanie klawisza Cos zawsze prowadziło do uzyskania pewnej liczby, która odtąd powtarzała się:

NestList[Cos,0.5,64]

Polecenia FixedPoint pozwala znaleźć tę liczbę:

FixedPoint[Cos[#]&,0.5]

Liczba ta to rozwiązanie równania Cos[x]==x:

FindRoot[Cos[x]==x,{x,0.5}]

Przykład: wszystkie możliwe złożenia funkcji opisanych wzorami  1, 1+x, i 1/x

Na koniec nieco bardziej złożony przykład. Chcielibyśmy ustalić, co może być wynikiem, powiedzmy 4-krotnego, złożenia trzech funkcji w dowolnej kolejności. Zaczynamy od nadania im nazw (dla czytelności):

jeden=1&

plus=1+#&

odwr=1/#&

Nasz zbiór funkcji składa się z 3 elementów:

funs={ jeden, plus,odwr}

Jako przygotowanie, wybierzmy losowo 4 z nich:

RandomChoice[funs,4]

Aby je poskładać, najwygodniej jest użyć polecenia Composition (ang. składanie). Polecenie to wymaga podania składanych funkcji oddzielonych przecinkami. Innymi słowy, Composition  posiada tyle argumentów, ile funkcji składamy. Aby zastosować funkcję do listy traktowanej jako osobne argumenty stosujemy operator @@:

Composition@@%

Aby obliczyć wynik składania, musimy podać jakiś argument powiedzmy x:

%[x]

Poleceniem Tuples możemy wygenerowa  wszystkie możliwe kombinacje:

t=Tuples[funs,4]

Zastosujmy przećwiczone na jednym przykładzie metody do każdego z elementów listy powyżej:

Table[(Composition@@t[[i]])[x],{i,1,Length[t]}]

Wiele elementów powtarza się, więc kasujemy duplikaty poleceniem DeleteDuplicates:

DeleteDuplicates[%]

Wyniki mają formę ułamków łańcuchowych. Sprawdźmy je do współnego mianownika:

Together[%]

i posortujmy:

Sort[%]

Okazało się, że w wyniku 4-krotnego składania podanych trzech funkcji mozna uzyskać 19 różnych wyników:

Length[%]

Funkcje odwrotne

Mathematica pozwala w dosyć sensowny sposób operować funkcjami odwrotnymi. Podstawowe polecenie to InverseFunction (ang: funkcja odwrotna):

InverseFunction[f]

Podstawowa definicja f. odwrotnej mówi, że złożona z oryginalem daje identyczność:

Composition[f,InverseFunction[f]][x]

f@InverseFunction[f]

%@x

InverseFunction[f][f[x]]

Wiele f. odwrotnych jest obliczana automatycznie:

InverseFunction[Sin]

W przypadku funkcji które nie są różnowartościowe, InverseFunction  zwraca tylko jedną, losowo wybraną, gałąź:

InverseFunction[#^2&]

Jak widać wyżej, nie jest to gałąź, której oczekiwaliśmy...Można sobie z tym poradzić zawężając dziedzinę:

InverseFunction[ConditionalExpression[#^2,#>0]&]

Więcej przykładów:

InverseFunction[#&]

InverseFunction[1/#&]

InverseFunction[Exp]

InverseFunction[#^#&]

InverseFunction[Exp[-#^2]&]

Funkcją odwrotną do wielomianu jest w ogólności Root:

InverseFunction[#^5+#+3&]

Funkcją odwrotną można się posługiwać nawet, gdy jej wzór nie istnieje:

inv=InverseFunction[Cos[#]-#&]

inv[Pi]

N[%]

Czasem udaje się nawet uzyskać ścisły symboliczne wynik:

inv[1]

Możemy taką funkcję rysować:

Plot[inv[x],{x,-4,4}]

Możemy obliczyć pochodną:

inv'[x]

inv'[1]

Próba rozwinięcia w szereg ujawnia niedopracowanie tej funkcji:

Series[inv[x],{x,1,2}]

Błąd programu polega na złym obliczaniu 2 pochodnej f. odwrotnej:

inv''[1]

N[%]