8. Anonymní funkce, funkcionální programování

Některé užitečné funkce pro práci se seznamy

First[{1,2,3}]

Last[{1,2,3}]

Rest[{1,2,3}]

Most[{1,2,3}]

Flatten[{{1,2,3},{4,5}}]

Anonymní funkce (pure functions)

Pozor na rozdíl mezi funkcí a výrazem (funkční hodnotou):

({{Funkce, Výraz}, {f, f(x)}, {sin, sin x}, {x↦(x^2), (x^2)}})  		({{Funkce, Výraz}, {f, f[x]}, {Sin, Sin[x]}, {Function[x,(x^2)]
Function[#^2]
#^2&, x^2}})

Příklad: Výběr prvočísel tvaru 4k+1 ze zadaného seznamu pomocí Select:

p[n_]:=(Mod[n,4]==1 && PrimeQ[n])

Select[Range[100],p]

Clear[p]

Kratší řešení pomocí anonymní funkce – 3 varianty zápisu:

Select[Range[100],Function[n,PrimeQ[n]&&Mod[n,4]==1]]

Select[Range[100],Function[PrimeQ[#]&&Mod[#,4]==1]]

Select[Range[100],(PrimeQ[#]&&Mod[#,4]==1)&]

Anonymní funkce více proměnných – názvy proměnných jsou #1, #2 atd.:

(#1+#2)&[3,5]

Funkcionální programování

Programátorské styly v Mathematice:

přepisovací pravidla

funkcionální programování

procedurální programování

Pro funkcionální programování je typická práce s anonymními funkcemi, seznamy a následujícími funkcemi:

Map a MapThread

Map[f,{x1,x2,x3}]

Map[Sqrt,{2,3,5}]

Ekvivalentní zápis:

Sqrt/@{2,3,5}

Použití Map je v tomto případě zbytečné, neboť Sqrt je Listable:

Sqrt[{2,3,5}]

Většina funkcí pro práci se seznamy není Listable:

Map[Reverse,{{a,b},{c,d}}]

Reverse[{{a,b},{c,d}}]

Map[Last,{{a,b},{c,d}}]

Last[{{a,b},{c,d}}]

Map se také často používá ve spojení s anonymními funkcemi.

Příklad: Převedení seznamu komplexních čísel na dvojice obsahující reálnou a imaginární složku.

complexToReal[s_List]:=Map[{Re[#],Im[#]}&,s]

complexToReal[{2+I,3,1-I}]

Příklad: Numerické řešení diferenciální rovnice x'(t)=-x(t)+t pro více různých počátečních podmínek.

reseni=Map[x[t]/.NDSolve[{x'[t]==-x[t]+t,x[0]==#},x[t],{t,0,5}]&,Range[0,4]]

Plot[reseni,{t,0,5},PlotStyle->Thick]

Clear[reseni]

MapThread je analogií Map pro funkce více proměnných:

MapThread[f,{{x1,x2,x3},{y1,y2,y3}}]

MapThread[Max,{{1,2,3},{3,2,1}}]

Příklad: Připojení vektoru jako nového sloupce k zadané matici.

pridejSloupec[M_,v_]:=MapThread[Append,{M,v}]

pridejSloupec[{{1,2},{3,4}},{5,6}]

Příklad: Nechť a,b>0. Cassiniho ovál je množina všech bodů v rovině, pro které je součin vzdáleností od bodů [-a,0] a [a,0] roven (b^2) (tj. je konstantní). Tvar Cassiniho oválu závisí na tom, zda a<b, a=b nebo a>b.

{ContourPlot[((x-1)^2+y^2)((x+1)^2+y^2)==0.95^4,{x,-2,2},{y,-1,1},AspectRatio->Automatic,ContourStyle->{Red,Thick},PlotLabel->"a<b"],
ContourPlot[((x-1)^2+y^2)((x+1)^2+y^2)==1^4,{x,-2,2},{y,-1,1},AspectRatio->Automatic,ContourStyle->{Red,Thick},PlotLabel->"a=b"],
ContourPlot[((x-1)^2+y^2)((x+1)^2+y^2)==1.05^4,{x,-2,2},{y,-1,1},AspectRatio->Automatic,ContourStyle->{Red,Thick},PlotLabel->"a>b"]}

Totéž elegantněji pomocí MapThread:

MapThread[ContourPlot[((x-1)^2+y^2)((x+1)^2+y^2)==#1^4,{x,-2,2},{y,-1,1},AspectRatio->Automatic,ContourStyle->{Red,Thick},PlotLabel->#2]&,{{0.95,1,1.05},{"a<b","a=b","a>b"}}]

Nest a NestList

Nest počítá iterace zadané funkce:

Nest[f,x,3]

Nest[Cos,1,3]

Obecněji: Nest se hodí pro počítání členů posloupností definovaných rekurentním vzorcem typu (Subscript[a, n+1])=f((Subscript[a, n])).

Příklad: Bankovní účet, úrok 3% p.a. připisován měsíčně, poplatky 30 Kč měsíčně. Vklad 20 000 Kč, spočítejte zůstatek po 12 měsících.

Řešení pomocí přepisovacích pravidel:

a[0]:=20000;
a[n_]:=((1+0.03/12)*a[n-1]-30)

a[12]

Clear[a]

Ekvivalentně pomocí Nest a anonymní funkce:

Nest[((1+0.03/12)*#-30)&,20000,12]

NestList[f,x,n] vrací seznam všech iterací od nulté po n-tou.

NestList[f,x,3]

NestList[((1+0.03/12)*#-30)&,20000,12]

Grafické znázornění:

ListPlot[NestList[((1+0.03/12)*#-30)&,20000,12*3],Filling->Axis,AxesLabel->{"měsíc","částka"}]

Interaktivní demo (budeme probírat později):

Manipulate[ListPlot[NestList[((1+urok/12)*#-poplatek)&,20000,pocet],PlotRange->{10000,37000},Filling->Axis,AxesLabel->{"měsíc","částka"}],{{urok,0.03,"úrok p.a."},0,0.1,Appearance->"Labeled"},{{poplatek,30},0,100,Appearance->"Labeled"},{{pocet,3*12,"počet měsíců"},12,6*12,1,Appearance->"Labeled"}]

Fold a FoldList

Fold a FoldList se hodí pro počítání členů posloupností definovaných rekurentním vzorcem typu (Subscript[a, n+1])=f((Subscript[a, n]),(Subscript[b, n])), kde {(Subscript[b, 1]),(Subscript[b, 2]), (Subscript[b, 3]), …} je zadaný seznam.

FoldList[f,x,{b1,b2,b3}]

Fold[f,x,{b1,b2,b3}]

Příklad: Účet s variabilní úrokovou mírou 0.03, 0.03, 0.02, 0.02, ...

Fold[((1+#2/12)*#1-30)&,20000,{0.03,0.03,0.02,0.02}]

FoldList[((1+#2/12)*#1-30)&,20000,{0.03,0.03,0.02,0.02}]