Wolfram Language I

Bardzo szybkie wprowadzenie do języka Wolframa.

Przykłady

Proste

2+2

2+Sin[π/2]

Trochę bardziej skomplikowane

f=Sin[x^2] x^3

Plot[f,{x,0,π},PlotRange->All,PlotTheme->"Detailed",Filling->Axis]

Integrate[f, x]

Integrate[f, {x,0,π}]

Integrate[Abs[f], {x,0,π}]

Jeszcze bardziej skomplikowane

g=x /(1+10 y^2)

ℛ=Disk[{0,0},1]

Plot3D[g,{x,y}∈ℛ,PlotTheme->"Detailed",PlotLegends->None,Filling->Axis,MaxRecursion->5]

Integrate[g,{x,y}∈Disk[{0,0},π]]

Integrate[Abs[g],{x,y}∈Disk[{0,0},π]]

𝒯=Disk[{1,0},1]

Plot3D[g,{x,y}∈RegionUnion[ℛ,𝒯],PlotTheme->"Detailed",PlotLegends->None,Filling->Axis,MaxRecursion->5]

Integrate[Abs[g],{x,y}∈RegionUnion[ℛ,𝒯]]

NIntegrate[Abs[g],{x,y}∈RegionUnion[ℛ,𝒯]]//Timing

Równania różniczkowe

sol1=DSolve[x''[t]==x'[t]+Sin[t]+Cos[2 t]-t,x[t],t]

sol2=DSolve[x''[t]==x'[t]+Sin[t]+Cos[2 t]-t && x[0]==0&&x'[0]==1,x[t],t]

f=x[t]/.First[sol1]

Manipulate[
Plot[f/.{C[1]->a,C[2]->b},{t, -1, 1},PlotRange->{-5,5}],
{a,-1,1},
{b, -1, 1}
]

Równania różnicowe i nie tylko

RSolve[x[t+2]==x[t+1]-x[t],x[t],t]

RSolve[x[t]==Sqrt[t] x[Sqrt[t]],x[t],t]

Probabilistyka

𝒟=NormalDistribution[0,1]

Probability[x-1≤0,x\[Distributed]𝒟]

Probability[x-1≤0,x\[Distributed]𝒟]//N

?Erfc

Probability[x^2-1≥x+1,x\[Distributed]𝒟]

Probability[x^2-1≥x+a,x\[Distributed]𝒟]

FullSimplify[Out[31], a>0]

Wyrażenia

Wprowadzenie

Wszystko jest wyrażeniem. Każde wyrażenie jest postaci head[arg1, arg2, ...].

{a,b,c}

Apply[Plus, {a,b,c}]

FullForm[{a,b,c}]

FullForm[a+b+c]

Aby obliczyć sumę elementów listy wystarczy zamienić List na Plus. Można to zrobić tak jak powyżej poprzez zastosowanie funkcji Apply.

Apply[Plus,{a,b,c}]

?Apply

Plus@@{a,b,c}

Trace[Apply[Plus,{a,b,c}]]

Oczywiście można to zrobić w odwrotnej kolejności.

Apply[List,a+b+c]

Ale poniższy przykład zwróci inny wynik.

Apply[List, 1+2+3]

Trzy typy wyrażeń

Wyrażenia non-atomic. Wyrażenie, które posiadają części, które można “wyjąć” przy pomocy takich funkcji jak Part oraz zamienić przy pomocy takich funkcji jak ReplacePart.

Part[{a,1,c},1]

{a,1,c}[[3]]

{a,1,c}[[3]]

Element zerowy dla tego typu wyrażeń to zawsze “głowa” wyrażenia.

{a,1,c}[[0]]

Elementy wyrażeń non-atomic można zamieniać na inne wyrażenia.

ReplacePart[{a,6,g},x+y, 2]

ReplacePart[a+b+c, 22, 1]

Wyrażenia atomic. Wyrażenia te to podstawowe wyrażenia tworzące język. Mamy 3 typy takich wyrażeń.

Symbole — składają się z litery, po której następują litery i cyfry, np. a34, test12A, π, e.

Liczby — całkowite (4, -3, 0), rzeczywiste (5.342, -4.23), zespolone (1-2 i), wymierne (3/4, 5/7)

FullForm[3]

FullForm[3.4564]

FullForm[1-2 I]

FullForm[3/4]

Head[3]

Stringi — wszystko co jest pomiędzy znakami “” (“ala ma kota”)

FullForm["ala ma kota"]

Head[%]

Zerowa część atomu jest typem atomu.

Head/@{1, 3.14, 1-3 I, 4/5,"Ala ma kota",DateObject[{2000, 1, 1}]}

Wyrażenia będące atomem nie mają części.

3[[1]]

(1+I)[[1]]

"ala ma kota"[[3]]

Ostatni typ wyrażeń to wyrażenia złożone (compund expressions). Wyrażenia te są postaci expr1; espr2; ...; exprn.

b=3+8; 2 b

Trace[b=3+8; 2 b]

Wszystkie wyrażenia są obliczane ale nie zawsze wypisywany jest wynik.

b=3+8;

%

%%

Out[74]

In[35]

In[32]

Obliczanie wyrażeń

Język Wolframa to system typu term rewriting system (TRS). Obliczenie w języku Wolfram’a polega na przepisaniu wyrażeń (term rewriting) zgodnie z regułami przepisania (rewrite rules). Każda taka reguła składa się z schematu (pattern) po lewej stronie i zastąpienia (replacement) po prawej stronie. Obliczenie polega na zastosowaniu wszystkich reguł, które są dostępne dla systemu w momencie dokonywania obliczenia.

Typowy przykład to obliczanie całek. Typowa reguła może wyglądać jak poniżej

∫(x^2)dx -> (1/3)(x^3)+C.

Możemy mieć również ogólniejsze formuły, np.

∫(((x_))^n)d(x_) -> (1/(n+1))(((x_))^(n+1))+C,

gdzie wyrażenie x_ jest schematem który nazywa się x ale może tam stać dowolne wyrażenie.

Ogólna pętla obliczeniowa jest (prawie dokładnie) następującej postaci.

Jeżeli wyrażenie jest liczbą lub stringiem to nie jest zmieniane.

Jeżeli wyrażenie jest symbolem to zostaje przepisane z zastosowaniem wszystkich dostępnych reguł w globalnej bazie reguł przepisań. Jeżeli żadna reguła nie jest stosowalna do tego symbolu to pozostaje on niezmieniony.

Jeżeli wyrażenie nie jest liczbą, stringiem ani symbolem to części tego wyrażenia są obliczane w następującej kolejności.

Głowa wyrażenia jest obliczana.

Argumenty wyrażenia są obliczane (z pewnymi wyjątkami) od lewej do prawej.

Po obliczeniu głowy oraz wszystkich argumentów wyrażenie wynikające z obliczonej głowy i argumentów jest zmieniane zgodnie z atrybutami (Attributes) a następnie są stosowane odpowiednie reguły przepisania.

Po wykonaniu powyższych kroków są one powtarzane tak długo aż dwie kolejne iteracje nie będą zmieniały wyrażenia.

W kroku 2 i 4 reguły przepisań działają w następujący sposób.

- część wyrażenia jest matchowana do patterna (lewej strony reguły)

- zmatchowane fragmenty wyrażenia są zamieniane na prawe strony reguły przepisań i obliczane

- wyniki obliczeń są wpisywane na zmatchowane fragmenty wyrażenia.

Mając powyższe możemy dokładnie zrozumieć w jaki sposób obliczane jest wyrażenie

Apply[List,1+2+3]

FullForm[Hold[Apply[List,1+2+3]]]

TreeForm[Hold[Apply[List,1+2+3]]]

Apply[List,Unevaluated[1+2+3]]

Powyższe działanie to tzw. pattern matching.

Patterns

Podstawy

Patterny dotyczą formalnej reprezentacji, tj. takiej, która jest podana przez FullForm. Wyrażenie zazwyczaj może być zgodne z wieloma patternami. Przykładowo, wyrażenie x^2 będzie pasowało do dowolnego z kolejnych patternów.

- x do potęgi 2
- x do potęgi liczba
- x do potęgi wyrażenie
- symbol do potęgi 2
- symbol do potęgi liczba
- symbol do potęgi wyrażenie
- wyrażenie do potęgi 2
- wyrażenie do potęgi liczba
- wyrażenie do potęgi wyrażenie
- wyrażenie

Każdy pattern jest zapisywany jako wyrażenie z tzw. blanks. Blanks mogą być postaci _, __, ___.  Każdy blank może być nazwany poprzez poprzedzenie go symbolem, np. name_, name__, name___. Po blanku może zostać również podany symbol, np. name_Integer. W takim przypadku aby wyrażenie pasowało do patternu musi mieć nagłówek (head) Integer.

Przykłady

MatchQ[x^2, x^2]

MatchQ[x^2, _]

MatchQ[x^2,x^_]

FullForm[x^2]

MatchQ[x^2,_Power]

Head[2]

MatchQ[x^2,x^_Integer]

Head[x]

MatchQ[x^2,_Symbol^2]

MatchQ[x^2,_^2]

MatchQ[x^2,_Symbol^_Integer]

MatchQ[x^2,_^_]

Nazwane blanki.

MatchQ[x^2,x^y_]

MatchQ[5^2,_^_]

Trace[MatchQ[5^2,_^_]]

MatchQ[Unevaluated[5^2],_^_]

Trace[MatchQ[Unevaluated[5^2],_^_]]

Bardziej skomplikowane patterny

Ciąg wyrażeni składa się z wyrażeń oddzielonych przecinkami, expr1, expr2, .....

Pojedynczy blank może być dowolnym wyrażeniem. Podwójny blank może być dowolną sekwencją jednego lub więcej wyrażeń. Potrójny balnk może być ciągiem zero lub więcej wyrażeń.

Symbol po znaku __, lub ___ oznacza, że wszystkie wyrażeni w ciągą muszą mieć nagłówek zgodny z podanym symbolem.

{FullForm[_], FullForm[_Integer],FullForm[x_Integer]}

{FullForm[__], FullForm[__Integer],FullForm[x__Integer]}

{FullForm[___], FullForm[___Integer],FullForm[x___Integer]}

Przykłady

MatchQ[{a,b,c},_]

MatchQ[{a,b,c},_List]

MatchQ[{a,b,c},__List]

MatchQ[{a,b,c},__]

MatchQ[{a,b,c},{_}]

MatchQ[{a,b,c},{__}]

Head[a]

MatchQ[{a,b,c},{__Symbol}]

MatchQ[{},{__}]

MatchQ[{},{___}]

MatchQ[{a,b,c},List[__]]

MatchQ[x^2,__]

MatchQ[x^2,___]

UWAGA: w zakresie matchowania patternów, ciąg wyrażeń nie jest wyrażeniem!

MatchQ[{a,b,c},{_}]

Jeszcze bardziej skomplikowane patterny / Przykłady

Alternatywy patternów.

MatchQ[x^2, {_}|_^2]

Testy. Po blanku można dodać test, np. _?test. W takim wypadku wyrażenie będzie pasowało do formuły tylko wtedy gdy będzie pasowalo do blanka i całe wyrażenie będzie spełniało test.

MatchQ[2,_]

MatchQ[2,_Integer]

MatchQ[2,_Integer?OddQ]

MatchQ[2,_Integer?EvenQ]

MatchQ[2,x_Integer?EvenQ]

MatchQ[2,_?((#>2)&)]

MatchQ[2,_?((#>1)&)]

MatchQ[{a,b,c},x_List?(Length[#]>2&)]

Warunki na części  wyrażenia. Jeżeli blanki mają nadane nazwy można nałożyć warunki na nazwane części poprzez składnię postaci /;condition. Taki pattern będzie pasował do wyrażenia jeżeli sam pattern będzie zgodny ale ponadto wszystkie warunki będą zwracały True.

MatchQ[x^2, _^y_]

MatchQ[x^2, _^y_/;Head[y]==Symbol]

MatchQ[x^2, _^y_/;Head[y]==Integer]

MatchQ[x^a, _^y_/;Head[y]==Symbol]

MatchQ[x^2,a_^b_/;(Head[a]==Symbol)&&(b>2)]

MatchQ[x^2,a_^b_/;(Head[a]==Symbol)&&(b>0)]

MatchQ[x^2,a_Symbol^b_/;b>0]

Kontrolowanie obliczania wyrażeń

Nie będziemy wchodzili w detale ale mamy funkcje, które pozwalają kontrolować zachowanie pętli obliczającej wyrażenia.

?*Hold*

?Evaluate

?Unevaluated

Reguły przepisywania

Wbudowanie reguły przepisywania

Wolfram Language posiada bardzo dużo wbudowanych reguł przepisań (funkcji). Typowe przykłady były podane wcześniej.

Sin[π/2]

Reguły przepisywania definiowane przez użytkownika

Reguły przepisań definiowane przez użytkownika są umieszczane w globalnej bazie reguł (z odpowiednimi kontekstami). Po zakończeniu sesji nie są one utrzymywane, trzeba je definiować na nowo (albo wgrać zapisane reguły — napisać skrypt).

Mamy dwie metody definiowania reguł przepisania. Pierwsza to wykorzystanie funkcji Set[], druga to wykorzystanie funkcji SetDelayed[].

?Set

?SetDelayed

Deklaracja wartości przy wykorzystaniu funkcji Set (=)

Typowe wykorzystanie funkcji Set[] jest postaci Set[lhs,rhs] albo w bardziej naturalnym zapisie lhs=rhs. Po prawej stronie może znajdować się wyrażenie zawierające patterny. Po prawej stronie jest zawsze wyrażenie albo sekwencja wyrażeń (nie może zawierać blanków).

Przykładowe reguły.

a={-1,1}

FullForm[Hold[a={-1,1}]]

rand1=RandomInteger[{1,2}]

Powyższe definicje zwracają wartości o ile nie są zakończone średnikiem. Reguły po definicji są używane, a więc jeżeli gdziekolwiek pojawi się symbol a to będzie zamieniony na listę {-1,1}.

Możemy sprawdzić jak wyglądają definicję reguł zdefiniowanych powyżej.

?a

?rand1

To co warto zauważyć, to wartości obu reguł są inne. W pierwszej sytuacji dostajemy dokładnie to co zostało wpisane, w drugiej sytuacji dostajemy wynik obliczenia z prawej strony. Funkcja Set lewą stronę pozostawia nieobliczoną podczas gdy prawa jest obliczana. Wynika to z atrybutów przypisanych do głowy Set.

Attributes[Set]

Pierwszy arguement nie jest obliczany.

?HoldFirst

?Protected

?SequenceHold

Zobaczmy przykład.

Set[a,7]

a

ClearAttributes[Set,HoldFirst]

Attributes[Set]

Set[a,6]

a

Otrzymujemy błąd setraw bo symbol a występujący jako pierwszy argument jest teraz obliczany, w wyniku tego obliczenia dostajemy przypisanie 7=6, które daje błąd.

7=6

Nadajemy ponownie artybut HoldFirst.

SetAttributes[Set,HoldFirst]

Attributes[Set]

Set[a,7]

a

?a

Wyrażenie po prawej stronie są obliczane od lewej do prawej, a więc w przypadku sekwencji wyrażeń to co jest przypisywane to wynik ostatniego obliczenia (jeżeli wszystko jest w nawiasach ())

rand2=(b={-1,1};RandomReal[b])

rand2

?rand2

Istotne jest to aby wyrażenia po prawej stronie były napisane w odpowiedniej kolejności.

Po zdefiniowaniu reguły jest ona wykorzystywana przy wykonywaniu obliczeń.

Sqrt[Abs[rand2]]

Takie same prawe strony mogą być przypisane do różnych lewych stron.

a=Array[#&,5]

b={1,2,3,4,5}

a==b

Zdefiniowane reguły można usuwać.

?Clear

?Remove

?"Global`*"

Clear[a]

a

?"Global`*"

Remove[a]

?"Global`*"

Remove["Global`*"]

?"Global`*"

?ClearAll

Deklaracja wartości przy wykorzystaniu SetDelayed (:=)

Druga możliwość definiowania przez użytkownika reguł to wykorzystanie funkcji SetDelayed[].

?SetDelayed

Attributes[SetDelayed]

Jak widać, różnica polega na zmianie atrybutu HoldFirst na atrybut HoldAll. Do lewej strony jest przypisana prawa strona ale jest ona trzymana w nieobliczonej formie.

a=RandomReal[]

a

b:=RandomReal[]

b

b

?a

?b

Połączenie patternów razem z opóźnionym przypisaniem jest podstawą definiowania funkcji. Typowa definicja funkcji jest postaci name[arg1_, arg2_, ..., argn_]:=(expr1; expr2; ...; exprm). Przykład takiej funkcji.

f[x_]:=RandomReal[{0,x}]

?f

f[10]

Wykorzystanie tak zdefiniowanej reguły jest standardowe. Poniżej tworzymy (10^3) liczb dla różnych wartości parametru.

a=Table[f[1],{k,1,10^3}];
b=Table[f[5],{k,1,10^3}];

cdfA=EmpiricalDistribution[a];
cdfB=EmpiricalDistribution[b];

Plot[{CDF[cdfA,x],CDF[cdfB,x]},{x, -1, 6},PlotTheme->"Detailed",PlotStyle->{Thick,Automatic}]

Short[a]

Short[b]

Short[a,3]

Co się dzieje jeżeli będziemy używali funkcji Set?

y=99

g[y_]=y

g[99]

g[0]

?g

Zobaczmy co się dzieje jeżeli zdefiniujemy funkcje g przy użyciu SetDelayed.

g[y_]:=y

?g

g[99]

g[0]

Poprzednia wartość została nadpisana i tak zdefiniowana funkcja działa poprawnie.

Jak wyglądają zmienne wewnątrz tak zdefiniowanych funkcji.

g[x_]:=(d=2;x+d)

?g

?d

Jak widać zmienna d nie jest zdefiniowana w żaden sposób ale baza wie, że istnieje taki symbol. Dzieje się tak dlatego, że prawa strona definicji g[x_] nie jest obliczona przy definiowaniu. Zostaje obliczona jedynie wtedy kiedy funkcja jest wykorzystana po raz pierwszy.

g[5]

?d

Teraz już jest zdefiniowane.

Wykorzystanie patternów przy definiowaniu reguł

Ponieważ na patterny można nakładać ograniczenia (pokazane wcześniej) więc można je również wykozystywać przy definiowaniu funkcji.

s[x_?EvenQ]:=x/2

s[3]

s[4]

W pierwszym przypadku nie ma reguły zamiany s[!liczba parzysta] więc wyrażenie nie jest zmieniane. W drugim przypadku jest reguła, która pozwala na zamianę.

Przykład funkcji działającej tylko na liczba dodatnich.

Remove[f]

f[x_/;((x>0))]:=Sqrt[x]

f[4]

f[-4]

?f

Dla liczb ujemnych możemy zdefiniować inną funkcję.

f[x_/;((x≤0))]:=Sin[x]

Plot[f[x],{x,-2 π,2π},PlotTheme->"Detailed",PlotLegends->None,GridLines->{{0}, {0}}]

Możemy również wykorzystać inne techniki.

h[x_Integer]:=x

h[x_Real]:=RandomReal[{Floor[x],Ceiling[x]}]

Plot[h[x],{x,0,5},PlotTheme->"Detailed",PlotLegends->None]

?h

Podobnie możemy wykorzystywać inne konstrukcje. Poniżej jest funkcja, która przyjmuje co najmniej jeden argument.

s[x__]:=Length[List[x]]

s[1,2,3]

Zmienne lokalne?

Zmienne można lokalizaować przez wykorzystanie funkcji Block[] oraz Module[].

?Block

?Module

Typowy przykład.

t[y_]:=
Module[
{local},
local=2;
y+local
]

t[1]

?local

Zmienna local jest w bazie ale nie ma wartości.

?"Global`local*"

t1[y_]:=
Module[
{local},
Print[local];
local=2;
y+local
]

t1[3]

?Temporary

Kolejność wyboru reguł

Jeżeli mamy zdefiniowane reguły, do których pasuje to samo wyrażenie, to powstaje problem, którą reguła powinna zostać użyta.

f[x_]:=x^2

f[x_Integer]:=x^3

f[2]

?f

Kolejność matchowania jest następująca: jako pierwsze są zawsze wykorzystywane reguły zdefiniowane przez użytkownika. Jeżeli wyrażenie pasuje do kilku reguł to wykorzystywana jest ta reguła, która jest najwęższa (most specific — i mniej wyrażeń pasujących do patternu tym bardziej wąski jest pattern). W powyższym przykładzie jest to reguła x_Integer.

f[2.454]

f[-2.45]

W powyższych przykładach jest to zawsze ostatnia reguła. Pierwsze dwie są efektywnie przesłaniane!

Jeżeli Wolfram Language nie jest w stanie określić, która reguła jest bardziej wąska wtedy wykorzystuje je w kolejności, w której są zapisane w bazie przepisań.

Reguły transformacji (transformation rules)

Czasami chcemy mieć “lokalną” regułę przepisywania, w tym sensie, że nie chcemy jej w globalnej bazie reguł. W takiej sytuacji można wykorzystać funkcje

?Replace*

oraz

?Rule

?RuleDelayed

Wykorzystanie Rule (->)

Typowe wykorzystanie jest postaci expr/.lhs->rhs.

Taka konstrukcja jest obliczana w następujący sposób: (a) wyrażenie, (b) lhs, (c) rhs (d) całość wyrażenia. Przykład.

Clear[a,b]

a+b/.a->2

a+b/.b->7

Table[
Table[1,{4}]/.RandomInteger[{0,1}]->a,
{10}
]

W zależności od tego jak zostanie obliczone RandomInteger[] dostajemy albo zamiany albo nie. Bardziej skomplikowany przykład.

Table[
Table[1,{4}]/.RandomInteger[{0,1}]->{a,b}[[RandomInteger[{1,2}]]],
{10}
]

W zależności od tego jak zostaną obliczone obie strony reguły albo zamiana nie zostaje przeprowadzona albo zostaje przeprowadzona na losowo wybrany symbol.

Reguły można podawać w liście jeżeli jest ich więcej: expr/.{lhs1->rhs1,lhs2->rhs2, ...}

{a,b,c}/.{c->b, b->a}

Powyższy przykład pokazuje jak jest stosowana lista reguł. W pierwszej kolejności szukana jest reguła na zamianę a. Takiej reguły nie ma więc a pozostaje a. Natępnie szukana jest reguła na zamiane b. Taka reguła istnieje i b jest zamieniane na a. Nastepnie szukana jest reguła na zamianę c, taka reguła istnieje i c jest zamieniane na b. Żaden element wyrażenia nie jest zamieniany wielokrotnie (!) o ile nie każdemy tego robić (dokładnie to co najwyżej jedna reguła jest wykorzystywana do zamiany jakiegokolwiek elementu wyrażenia). A więc po zamianie c na b nie jest ponownie wykorzystywana reguła b->a.

Możemy wykorzystać wielokrotne zamiany ReplaceRepated[].

?ReplaceRepeated

{a,b,c}//.{c->b, b->a}

{a,b,c}//.{b->a,c->b}

Wykorzystanie RuleDelayed (:>)

Typowe wykorzystanie RuleDelayed jest w formacie expr/.lsh:>rhs albo w postaci listy expr/.{lhs1:>rhs1, lhs2:>rhs2,...}.

Reguła taka jest obliczana w nastepującej kolejności: (a) obliczenie wyrażenia (b) obliczenie lewej strony reguły (c) zamiana (d) obliczenie powstałego wyrażenia.

Table[
Table[1,{4}]/.RandomInteger[{0,1}]:>{a,b}[[RandomInteger[{1,2}]]],
{5}
]

W powyższym przykładzie na 1, 2 i 5 miejscu wyrażenie RandomInteger[{0,1}] zostało obliczone do 0 więc nie następiło żadne zastąpienie. Na miejscach 2 i 3 wyrażenie to zostało obliczone do 0 i nastąpiły zastąpienia bezpośrednio na wyrażenie {a,b}[[RandomInteger[{1,2}]]], które zostało obliczone na samym końcu a więc w ramach jednego zastąpienia możemy mieć różne symbole.

Ograniczenia

Tak jak poprzednio możemy wykorzystać ograniczenia i warunki na patterny, które stosujemy w ramach reguł transformacji.

Remove[a]

a + 2 a^2 + 3 a^5/._^_->ξ

a + 2 a^2 + 3 a^5/._Integer^_->ξ

a + 2 a^2 + 3 a^5/._^_Integer->ξ

a + 2 a^2 + 3 a^5/._^_Integer?EvenQ->ξ

a + 2 a^2 + 3 a^5/._^x_Integer/;(x>4)->ξ

a + 2 a^2 + 3 a^5/._^x_:>RandomReal[{x-1,x}]

a + 2 a^2 + 3 a^5/._^x_:>RandomReal[{x-1,x}]/;x>4

Dodawanie ograniczenia w Rule[] po prawej stronie jest bez sensu bo jest ono obliczane przed zastąpienie.

Podsumowanie

Style programowania

Podstawowym stylem programowania wydaje się być “functional style” w odróżnienia od “procedural style” (C, Python ale nie R). LISP ( (funkcja arg 1arg2 arg3 ))

Nested function calls

Wykonanie w trzech krokach

Tan[4.]

Sin[%]

Cos[%]

Jedno wywołanie.

Cos[Sin[Tan[4.]]]

Cos@Sin@Tan[4.]

Cos@Sin@Tan@4.

W powyższym przykładzie nie definiujemy zmiennych do przechowywania “pośrednich” wartości.

Przykład. Talia kart.

Range[2,10]

Join[%,{𝒥,𝒬,𝒦,𝒜}]

Outer[List,{♣,♢,♡,♠},%]

Flatten[%,1]

Powiązanie w jedno wywołanie.

deck=Flatten[Outer[List,{♣,♢,♡,♠},Join[Range[2,10],{𝒥,𝒬,𝒦,𝒜}]],1]

Operator tasowania.

Last@Transpose[Sort@Transpose[{RandomReal[{0,1},52],deck}]]

Inne podejście to operatora tasowania. Tutaj samo tasowanie polega na tym samym ale inaczej wydzielamy karty od pozycji.

Sort@Transpose[{RandomReal[{0,1},52],deck}]/.{_,x_}:>x

Anonimowe funkcje

Wolfram Language pozwala na tworzenie anonimowych funkcji (pure functions, lambda functions).  Typowa składnia to (#1, #2, ...)&.

q[x_]:=x^4

q[5]

(#^4)&[5]

FullForm[(#^4)&]

(#1^#2)&[5,2]

Ważne jest aby rozumieć liczbę argumentów.

(#1^#2)&[{5,3}]

(#[[1]]^#[[2]])&[{5,3}]

Anonimowe  funkcje można zagnieżdżać ale w takim przypadku wygodniej jest wykorzystać pełny zapis Function[{x,y,...},body]

Function[x,x^3][3]

Function[x,x^3][Function[x,x+4][4]]

TreeForm@Hold[Function[x,x^3][Function[x,x+4][4]]]

Oczywiście można mieszać zapisy.

Function[x,x^3][(#+4)&[4]]

TreeForm@Hold[Function[x,x^3][(#+4)&[4]]]

Higher-order functions

Funkcje, które jako argumenty przyjmują inne funkcje i nimi manipulują. Poniżej jest przegląd typowych i najczęście wykorzystywanych tego typu funkcji.

Apply

?Apply

Remove[f]

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

f@@{{a,b},{c,d}}

Apply[f,{{a,b},{c,d}},2]

f@@@{{a,b},{c,d}}

Forma operatorowa.

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

Map

?Map*

?Map

Remove[a,b,c,d]

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

f/@{a,b,c,d}

Remove[g]

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

Map[g,{{a,b},{c,d}},{1}]

Map[g,{{a,b},{c,d}},{2}]

Uwaga, można nadać funkcji atrybut Listable co będzie dawało automatycznie rozkładanie na elementy listy.

Attributes[Log]

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

MapThread

?MapThread

MapThread[g,{{a,b},{c,d}}]

MapThread[List,{{a,b},{c,d}}]

MapThread[Plus,{{a,b},{c,d}}]

NestList i Nest

?Nest

?NestList

NestList[Sqrt,10.^4,10]

Nest[Sqrt,10.^4,10]

Realizacja modelu AR(1).

model[x_]:=0.5 x + RandomVariate[NormalDistribution[0,1]]

ListPlot[
NestList[model,0,100],
PlotTheme->"Detailed",
Filling->Axis
]

FixedPointList i FixedPoint

?FixedPoint

?FixedPointList

FixedPointList[Sqrt,10^4,SameTest->(Abs[#1-#2]<10^-10&)]

ListPlot[%,PlotTheme->"Detailed"]

W powyższym przykładzie #2 to ostatni element a #1 to przedostatni element.

Fold i FoldList

?Fold

?FoldList

FoldList[Plus,0,{a,b,c,d}]

Thread

?Thread

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

Thread[f[{a,b,c,d},x]]

Remove[a,b,c,x,y,z]

Thread[{a,b,c}=={x,y,z}]

Ten ostatni przykład jest bardzo wygodny do tworzenia warunków.

?Through

Through[{f, h, h}[x]]