Evropský sociální fond Praha & EU : Investujeme do vaší budoucnosti
Programování v Mathematice: přednášky 7 Ing. Ladislav Musil, Ph.D. ČVUT v Praze
BI - PMA Zimní semestr 2011 © Ladislav Musil 2011
2
Pr7.nb
ü 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 2a
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
8s, sd, ss, d, s, s, s, ss< ê. s → d
8d, sd, ss, d, d, d, d, ss<
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).
8s, sd, ss, d, s, s, s, ss<
8s, sd, ss, d, s, s, s, ss< d=s s
8s, sd, ss, d, s, s, s, ss<
8s, sd, ss, s, 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 Set::write : Tag Plus in a + b is Protected. à
3 3@a, bD = 5 Set::write : Tag Integer in 3@aD is Protected. à
5 _=2 Set::nosym : _ does not contain a symbol to attach a rule to. à
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
Pr7.nb
3
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@sD, s, 8s<, s@sD, 8s@D<, ssD ê. List@sD → h
g@h, s, h, s@sD, 8s@D<, ssD
patt = s; g@List@sD, s, 8s<, s@sD, 8s@D<, ssD ê. 8patt< → h
g@h, s, h, s@sD, 8s@D<, ssD
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 ê. 8x → r, x → t< r
x ê. 8x → x, x → r< x
x ê. 8x → r, x → x< r
x ê. 8x → r, r → u< r
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 ê. 8x → r, r → u< r
x êê. 8x → r, r → u< u
Pozor však na zacyklení
x êê. 8x → r, r → x<
ReplaceRepeated::rrlim : Exiting after x scanned 65536 times. à
x
x ê. 8x → r, r → x< r
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, kD ê. Plus@k, nD → 4 4
Plus@n, k, lD ê. Plus@k, n, lD → 4 4
4
Pr7.nb
ü 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" .
8s, sd, ss + 3, d, 8s<, 8 s, s<, ss< ê. _ → h 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ř.:
8s, sd, 8ss + 3<, d, 8s<, 8 s, s<, ss< ê. 8_< → h
8s, sd, h, s, h, 8s, s<, ss<
f@s, sd, f@ss + 3D, d, f@sD, f@ s, sD, ssD ê. f@_D → h
f@s, sd, h, d, h, f@s, sD, ssD
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@D, f@sD, f@ s, sDD ê. f@sD → h f@f@D, h, f@s, sDD
f@f@D, f@sD, f@ s, sDD ê. f@s ..D → h f@f@D, h, hD
f@f@D, f@sD, f@ s, sDD ê. f@s ...D → h f@h, h, hD
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@D, f@D, f@DD ê. f@D ... → h g@h, h, hD
g@f@D, f@D, f@DD ê. f@D → h g@h, h, hD
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@D, f@sD, f@ s, tDD ê. f@_..D → h Syntax::sntxf : "f@_.." cannot be followed by "D".
Syntax::tsntxi : "_.." is incomplete; more input is needed. Syntax::sntxi : Incomplete expression; more input is needed.
g@f@D, f@sD, f@ s, tDD ê. f@H_L ..D → h
g@f@D, h, hD
Abychom nemuseli psát zdlouhavě (_).. nebo (_)... je pro tyto znaky v Mathematice zavedena často využívaná zkratka __ resp. ___
g@f@D, f@sD, f@ s, tDD ê. f@__D → h g@f@D, h, hD
Pr7.nb
5
g@f@D, f@sD, f@ s, tDD ê. f@___D → h g@h, h, hD
Pro tvoření alternativních patternů je možné v patternech používat logické zapsané znakem |
g@f@sD, f@tD, f@ s, tD, f@ t, tD, f@s, uDD ê. f@s
tD → h
g@h, h, f@s, tD, f@t, tD, f@s, uDD
g@f@sD, f@tD, f@ s, tD, f@ t, tD, f@s, uDD ê. f@Hs
tL ..D → h
g@h, h, h, h, f@s, uDD
Existuje i logické ne pro patterny
g@f@sD, f@tD, f@uD, f@ s, tDD ê. f@Except@sDD → h g@f@sD, h, h, f@s, tDD
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@sD, f@tD, f@πD, f@ 3D,
[email protected] ê. f@_ ? NumberQD → h g@f@sD, f@tD, f@πD, h, hD
g@f@sD, f@tD, f@πD, f@ 3D,
[email protected] ê. f@_ ? NumericQD → 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@sD, f@tD, f@πD, f@ 3D,
[email protected] ê. f@_ ? IntegerQD → h g@f@sD, f@tD, f@πD, h,
[email protected]
g@f@sD, f@tD, f@πD, f@ 3D,
[email protected] ê. f@_IntegerD → h g@f@sD, f@tD, f@πD, h,
[email protected] Head@3D Integer
g@f@sD, f@tD, f@πD, f@ 3D,
[email protected] ê. _f → h g@h, h, h, h, hD
g@f@s, tD, f@tD, f@D, f@ 3, gD,
[email protected] ê. f @___SymbolD → h g@h, h, h, f@3, gD,
[email protected]
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, tD, f@tD, f@D, f@ 3, gD,
[email protected] ê. f@d : _D → d + 3 g@3 + s + t, 3 + t, f@D, 6 + g, 8.D g@d : _D = d + 3 3+d g@aD 3+a
6
Pr7.nb
ClearAll@gD
Pro pro velice často používanou sekvenci jméno:_ existuje zkratka jmeno_ g@d_D = d + 3 3+d g@aD 3+a