Mathematica kurz

Mathematica pracuje s takzvanými atomickými a se složenými výrazy. Atomický je výraz, který nelze rozložit na několik podvýrazů.

Atomické výrazy jsou několika typů. Mezi nejdůležitější typy patří Real, Integer, String a Symbol. 

Typ Symbol je něco, co je nového oproti běžným programovacím jazykům. Bylo by možné to přeložit jako značka, nebo označení, no prostě je to symbol. Tohoto typu je jakýkoliv atomický výraz, který nenáleží některému z ostatních typů. Naproti tomu Mathematica nezavádí klasický typ proměnné, jako místo kam něco ukládáme, ale vystačí si se Symboly a k nim přiřazenými pravidly nahrazení. Prakticky se pak symboly mohou chovat stejně jako proměnné v ostatních jazycích, ale zároveň lze s nimi dělat mnohem víc a celý systém je velice konzistentní.

Typ symbolu je možné získat funkcí Head

Head[5]

Head[5.]

Head["5"]

Head

Složené výrazy jsou stromovou strukturou složenou z atomických výrazů. Každá větev této struktury je tvořena (možno chápat i jako označena ) aomickým výrazem. Jinými slovy: Ke každému atomickému výrazu lze přidat podvýrazy (podvětve). Syntakticky je to prakticky tak, že napíšeme atomický výraz a za něj do hranatých závorek podvýrazy (větve složeného výrazu). Tak např.

symb[neco,"string"[ratata, na, vrata[klika]],1[5,"str",symb2]]

symb[neco,"string"[ratata, na, vrata[klika]],1[5,"str",symb2]]//TreeForm

Píkazem Head dostaneme atomický výraz označující kořen struktury výrazu (takzvanou hlavičku) jednotlivé větve i podvětve dostaneme pomocí dvojitých hranatých závorek.

symb[neco,"string"[ratata, na, vrata[klika]],1[5,"str",symb2]]//Head

symb[neco,"string"[ratata, na, vrata[klika]],1[5,"str",symb2]][[2]]

%//TreeForm

symb[neco,"string"[ratata, na, vrata[klika]],1[5,"str",symb2]][[2,3]]

symb[neco,"string"[ratata, na, vrata[klika]],1[5,"str",symb2]][[0]]

symb[neco,"string"[ratata, na, vrata[klika]],1[5,"str",symb2]][[2,3,0]]

Některé symboly použité jako hlavička (označení kořenu) výrazů určité struktury (u některých se například chce, aby měly alespoň dvě podvětve) mají specifický význam a některé se pak dokonce standartně v notebooku zobrazují úplně jinak. Například Plus, Minus, Times, Power, List....:

List[1,Plus[Times[2,c],Minus[z]],Plus[3,a],Power[x,b]]

{1,2 c-z,3+a,(x^b)}//TreeForm

Větve ve stromu výrazu mají své pořadí, tak jak je píšeme za sebou do hranatých závorek. Někdy ale chceme, aby byl daný výraz komutativní (např. Plus, Times...) Takové symboly mají nastaven příznak Orderless

Attributes[Plus]

Mathematica se pak stará o to, aby dva výrazy s opačným pořadím se chovali jako totožné. Jeden za základních způsobů, jak to dělá, je, že když narazí na složený výraz s hlavičkou s nastaveným attributem Orderless, tak všechny jeho podvětve nejprve uspořádá. Jelikož vždy uspořádává podle stejného klíče, mají komutované výrazy vždy po uspořádání stejné pořadí podvětví a jsou tedy totožné.

a+b

b+a

Existuje mnoho attributů pro symboly a je možné je používat a přiřazovat svým novým symbolům. Popis jednotlivých attributů najde uživatel v nápovědě.

SetAttributes[ss,Orderless]

ss[a,b]

ss[b,a]

ss[a,b,c]==ss[c,b,a]

Pravidla a vzory

Mathematica funguje na pravidlech: Zadavame stale pravidla typu, když najdeš toto nahraď to tímto. V Mathematice je předepsána spousta předdefinovaných pravidel jako například: když najdeš součet dvakrát toho samého nahraď to pomocí násobení dvou

a+a

Máme velice silné nástroje, jak zadávat pravidla vlastní. Pravidla mohou být buď pro jednorázové, popř. jen pro několikanásobné použití, nebo pravidla s trvalou platností. 

Pravidla jednorázová zapisujeme pomocí zkratky -> nebo :> a na určitý výraz aplikujeme pomocí zkratky /. nebo //. . Tak např. pravidlo, když najdeš symbol s nahraď ho symbolem d napíšeme

s->d

Toto pravidlo (anglicky Rule) aplikujeme na množinu (anglicky List) symbolů takto

{s,sd,ss,d,s,s,s,ss}/.s->d

Pravidla travalá se zapisují pomocí zkratek = a := . Od chvíle kdy je odešleme se aplikují se automaticky na všechno, co necháme vyhodnotit (pošleme pomocí Ctrl-Enter na zpracování do kernelu).

{s,sd,ss,d,s,s,s,ss}

d=s

{s,sd,ss,d,s,s,s,ss}

Pozn. 0 : Pozor trvalá pravidla se vnitřně defaultně přiřazují kořenovému symbolu (k hlavičce) v reprezentaci výrazu na levé straně pravidla (viz. FullForm). Tento symbol musí tedy ve vzoru existovat a nesmí být chráněn proti novým pravidlům (attribut Protected)

a+b=3

3[a,b]=5

_=2

Pozn. 1: I na funkce se můžeme dívat jako na zadefinovaná pravidla (kdykoli kdy najdete něco ve tvaru f[ ... ] nahraď to něčím a to předem také vyhonoť, tedy aplikuj na to všechna známá pravidla ). 
Pozn. 2: Podobně i výpočty jsou jen sadou pravidel pro nahrazení: Kdykoliv uvidíš součet nahraď to výsledkem součtu (ano zde je to realizováno v kernelu jako funkce protože pravidlel pro všechny možné součty je nekonečně mnoho, ale navenek se vše chová jako aplikace předdefinovaných pravidel)

To, co je na levé straně pravidla, tedy to co hledáme a s čím porovnáváme, je právě vzor (neboli pattern). Tedy přesněji: To, co je na levé straně pravidla, tedy to co hledáme a s čím porovnáváme, je předpokládáno býti vzorem (neboli patternem). Vzor (pattern) totiž může existovat i osamoceně, aniž by byl aplikován na levou stranu pravidla, či jinam, kde je vzor předpokládán. Je to pak, ale vlastně jen normální výraz jako každý jiný. Teprve při aplikaci, tedy ve chvíli, kdy je s ním něco porovnáváno (ať už v rámci aplikace pravidla, nebo třeba funkce MatchQ) dostanou některé symboly v něm zvláštní význam.

g[List[s],s,{s},s[s],{s[]},ss]/.List[s]->h

patt=s;
g[List[s],s,{s},s[s],{s[]},ss]/.{patt}->h

Při aplikaci nahrazení, Mathematica prochází postupně od kořene stromovou strukturu výrazu na který aplikujeme a porovnává každou větev a její podstrukturu s patternem. Když na patternu struktura odpvídá, provede nahrazení. Nahrazení můžeme aplikovat i více na jednou, ale Mathematica při použití /. stále prochází strukturu jen jednou a v každé úrovni postupně provádí porovnání s jednotlivými patterny. Když najde první vyhovující pattern, provede nahrazení a zbylé patterny už nezkouší. Do výrazu který dosadila na místo původního se poté už mathematica nekouká. Proto např.

x/.{x->r,x->t}

x/.{x->x,x->r}

x/.{x->r,x->x}

x/.{x->r,r->u}

Jestliže chceme, aby všechna pravidla byla aplikována i na nové struktury výrazů, které se objevily ve výrazu po nahrazení, použijeme místo /. příkaz //.  . Tento příkaz projíždí stromovou strukturu a aplikuje nahrazení tolikrát, dokud není po posledním průchodu výraz shodný jako před ním (žádné pravidlo už nebylo aplikováno).

x/.{x->r,r->u}

x//.{x->r,r->u}

Pozor však na zacyklení

x//.{x->r,r->x}

x/.{x->r,r->x}

Jestliže je některý výraz komutativní (Symbol označující jeho hlavičku má nastaven attribut Orderless), zkouší Mathematica jakékoli variace jeho podvětví a opužitje první, který padne.

Plus[n,k]/.Plus[k,n]->4

Plus[n,k,l]/.Plus[k,n,l]->4

Obecné patterny

Nyní si probereme některé symboly, které mají při aplikaci patternu zvláštní význam:
 
Nejdůležitějším symbol v patternu je _ podtržítko (Blank). Tento symbol lze přeložit jako: "cokoli" .

{s, sd, ss+3, d, {s},{ s, s}, ss} /. _-> h

Je ovšem důležité si uvědomit, že podtržítko nahrazuje vždy pouze jednu větev stromové struktury výrazu. Proto např.:

{s, sd, {ss+3}, d, {s},{ s, s}, ss} /. {_}-> h

f[s, sd, f[ss+3], d,f[s],f[ s, s], ss] /. f[_]-> h

Je tedy podtržítko to, co označuje jakoukoliv větev struktury výrazu (tou může být i kořen struktury, takže samotné podtržítko nahradí jakýkoli výraz, protože každý výraz má kořen )

Dalšími dvěma symboly jsou dvě tečky a tři tečky. Oba značí opakování patternu. Dvě tečky za patternem značí jednu, nebo víc podvětví jdoucích za sebou a splňujících tento pattern. Tři tečky značí nula, jedna, nebo víc. Opakování se většinou používá na podpattern hlouběji ve struktuře výrazu tvořící pattern.

f[f[],f[s],f[ s, s]] /. f[s]-> h

f[f[],f[s],f[ s, s]] /. f[s..]-> h

f[f[],f[s],f[ s, s]] /. f[s...]-> h

Použijeme-li opakování na celý pattern (na kořen výrazu a ne na některý podpattern), nahradí se každá z nalezených odpovídajících podvětví zvlášť a výsledek bude, jako bychom opakování nepoužili.

g[f[],f[],f[]] /. f[]...-> h

g[f[],f[],f[]] /. f[]-> h

Bohužel nelze zapsat pomocí dvou/tří teček opakování patternu podtržítko přímo, ale podtržítko musíme dát do závorek, protože znak podtržítko tečka je už v syntaxi Mathematicy rezervován pro jinou funkci.

f[f[],f[s],f[ s, t]] /. f[_..]-> h

g[f[],f[s],f[ s, t]] /. f[(_)..]-> h

Abychom nemuseli psát zdlouhavě (_).. nebo (_)... je pro tyto znaky v Mathematice zavedena často využívaná zkratka __ resp.  ___

g[f[], f[s], f[ s, t]] /. f[__] -> h

g[f[], f[s], f[ s, t]] /. f[___] -> h

Pro tvoření alternativních patternů je možné v patternech používat logické zapsané znakem |

g[f[s], f[t], f[ s, t], f[  t,t],f[s,u]] /. f[s|t] -> h

g[f[s], f[t], f[ s, t], f[  t,t],f[s,u]]  /. f[(s|t)..] -> h

Existuje i logické ne pro patterny

g[f[s], f[t], f[u],f[ s, t]]  /. f[Except[s]] -> h

Logické and lze použít mezi patternem a doplňující podmínkou, což je funkce, do které můžeme dosadit to co chceme nahradit (co testujeme), a vypadne atomický typ Boolean. Je to tzv. podmíněný pattern, protože pattern vrátí, že pasuje, jen když je navíc splněna dodatečná podmínka. Značí se otazníkem

g[f[s], f[t], f[π], f[ 3],f[5.]]  /. f[_?NumberQ] -> h

g[f[s], f[t], f[π], f[ 3],f[5.]]  /. f[_?NumericQ] -> h

Tak jak bylo popsáno je možné pomocí vytvoření tesotvací fce vytvořit téměř jakoukoliv podmínku. Často je ale nutné testovat pattern (nebo spíš tím patternem testovaný výraz) na to jakou má hlavičku. Připomeňme, že hlavičku dostaneme fcí Head (popř. též pomocí [[0]]) a u složené funkce se nám vrátí atomický výraz označující kořen výrazu a u atomického výrazu dostaneme typ (tedy atomický výraz typu Symbol označující typ). Lze to udělat tak, že vyvoříme testující fci na daný typ a použijeme ? Proto existuje zkratka _JmenoHlavičky, která lze přeložit jako cokoli, co má hlavičku JmenoHlavičky.

g[f[s], f[t], f[π], f[ 3],f[5.]]/. f[_?IntegerQ] -> h

g[f[s], f[t], f[π], f[ 3],f[5.]]/. f[_Integer] -> h

Head[3]

g[f[s], f[t], f[π], f[ 3],f[5.]]/. _f -> h

g[f[s,t], f[t], f[], f[ 3,g],f[5.]]/. f [___Symbol]-> h

Občas můžeme při nahrazení chtít na pravé straně (to co bude místo starého), použít část toho, co chceme smazat (nahradit). Proto můžeme část patternu (nebo klidně i celý) pojmenovat a napravé straně se nám pod tímto lokálním označením objeví to co bylo označenou částí patternu nahrazeno. Označení se provádí pomocí jméno:pattern

g[f[s,t], f[t], f[], f[ 3,g],f[5.]]/.f[d:_]->d+3

g[d:_]=d+3

g[a]

ClearAll[g]

Pro pro velice často používanou sekvenci jméno:_ existuje zkratka jmeno_

g[d_]=d+3

g[a]