Úvod do programování v Mathematice

Mathematica je expertni systém

Vše o čem můžete přemýšlet je List.

Vše, co můžete vymyslet jsou pravidla pro operace nad těmito listy.

Vše veliké, co bylo lidmi vymyšleno jsou pravidla pro operace které se nám líbí skoro všem nad Listy, které se nám líbí skoro všem.

Procedurální programování

Programování, jako v jazycích Java, C, Matlab, Fortran a pod.

Funkce, moduly

Definice funkce

Clear[plocha]
plocha[r_] := Pi (r^2)
plocha[3]

Bez podtržítka to nefunguje

Clear[plocha,  r]
plocha[r] := Pi (r^2)
plocha[3]
plocha[r]

?plocha

plocha[r] =.

?Global`*

Funkce může mít víc příkazů oddělených středníkem (;)
Příkazy funkce jsou ohraničeny kulatými závorkami.
Funkce vrací výsledek posledního příkazu (pokud není ukončen středníkem) nebo argument volání Return[]

d = 5
quad[a_, b_, 
        c_] := (
d = 
          Sqrt[(b^2) - 4  a c]; 
{-b + d, -b - 
              d}/(2  a)
)
quad[1, -3, 2]
d

V předchozím příkladě byla přepsána globální proměnná d. Chceme-li aby d byla lokální proměnná použijeme Module[{seznam lokálních symbolů}, body]

d = 5
quad[a_, b_, 
        c_] := 
Module[{d,  
          e = 2  a}, 
d = 
          Sqrt[(b^2) - 
              4  a c]; 
Return[{-b + d, -b - d}/
            e];
]
quad[1, -3, 2]
d

Jinou možností je použití With[{seznam lokálních symbolů}, body]

d = 5
quad[a_, b_, 
        c_] := 
With[{d = Sqrt[(b^2) - 4  a c],  
          e = 2  a}, 
Return[{-b + d, -b - d}/
              e];
]
quad[1, -3, 2]
d

Předávání parametrů

Volání hodnotou
Parametry se před nahrazením vyhodnotí.

suma[n_] := 
      Module[{s = 0,  x = n}, 
While[
          x > 0, 
s += 
            x; x--
]; 
Return[
          s]
]
suma[33]

Volání jménem (Algol-60)
Parametry se před nahrazením nevyhodnotí (HoldAll).

SetAttributes[inc,  HoldAll]
inc[x_] := x = x + 1;
p = 3
inc[p]
p

Volání odkazem (referencí)
Mathematica nezná nic jako pointer nebo referenci, proto je realizace tohoto předávání poněkud tajemná.

SetAttributes[swap,  HoldAll]
swap[a_Symbol, 
        b_Symbol] := 
Module[{temp}, 
temp = a; a = b; 
        b = temp;
]
{p, q} = {1, 2}
swap[p, q]
swap[3, 4]
{p, q}

swap[1, q]

Zapouzdření (scoping)

Module a With používá lexikální scoping
To znamená, proměnná a uvnitř modulu překryje globální a. Je to jiná proměnná. Používa se i v C nebo Pascalu.
Block používá dynamický scoping
Pro lokální proměnné se vytváří dočasné hodnoty, ne nové symboly. Na první pohled to vypada stejně, ale ...

jakoX := x;
x = 1; jakoX
x = 2; jakoX
Module[{x = 3},  jakoX]
Block[{x = 3},  jakoX]
jakoX
x

suma[n_] := 
      Module[{s = 0, i = n}, 
While[i > 0, 
          s += i; i--]; 
s
]
suma[33]

Řízení běhu programu

If[condition, t, f] pokud je podmínka True rovná se t, pokud False rovná se f, jinak se nevyhodnotí a zůstane nezměněna.
If[condition, t, f, u] gives u if condition evaluates to neither True nor False.
Switch[expr, (Subscript["\!\(\*StyleBox[\"\\\"form\\\"\", \"TI\"]\)", 1]), (Subscript["\!\(\*StyleBox[\"\\\"value\\\"\", \"TI\"]\)", 1]), (Subscript["\!\(\*StyleBox[\"\\\"form\\\"\", \"TI\"]\)", 2]), (Subscript["\!\(\*StyleBox[\"\\\"value\\\"\", \"TI\"]\)", 2]), … ] vyhodnotí expr a výsledek porovnává postupně s form, vrátí první value, pro kterou form platí.
Which[(Subscript["\!\(\*StyleBox[\"\\\"test\\\"\", \"TI\"]\)", 1]), (Subscript["\!\(\*StyleBox[\"\\\"value\\\"\", \"TI\"]\)", 1]), (Subscript["\!\(\*StyleBox[\"\\\"test\\\"\", \"TI\"]\)", 2]), (Subscript["\!\(\*StyleBox[\"\\\"value\\\"\", \"TI\"]\)", 2]), … ] vyhodnocuje jeden test za druhým a vrací value u testu, který se první vyhodnotí jako True.

If[7 > 8, x, y]
If[z > 8, x, y]
If[z > 8, a, b, c]

Plot[
        Which[x < 0, x, x < π, Sin[x],  x ≥ π, 
          Cos[x]], {x, -2 π, 2  π}];

Do[expr, {i, imin, imax}, {j, jmin, jmax}, … ]

Do[Plot[Sin[n x], {x, 0, 2  π}], {n, 1, 20, 3}]

(* 
      obrazky si nejprve vypocitame a pak zobrazime *) 
obrazky = 
      Table[Plot[Sin[n x], {x, 0, 2  π}, 
          DisplayFunction -> Identity], {n, 1, 20, 
          3}]

    Show[obrazky[[2]], 
      DisplayFunction -> $DisplayFunction]

    Show[obrazky, DisplayFunction -> $DisplayFunction]

While[test, body]
Break[]
Continue[]

n = 10; 
    While[(n = n - 1) > 0, If[n < 3, Continue[]]; Print[n]]

For[start, test, incr, body]

ClearAll[x]
For[i = 1; t = x, (i^2) < 10, i++, t = t + i; Print[t]]

Příklady

Faktorial

factproc[n_] := 
      Module[{fs = 1}, 
For[i = 1, 
          i ≤ n, i++, 
          fs  =  fs * i]; 
Return[
          fs];
]
Timing[1. *factproc[50000]]
Timing[1. *50000!]

Funkcionální programování

Nejdůležitější operace

Map[f, list] (/@)
Namapuje funkci f postupně na všechny prvky listu. Výsledkem je nový list, původní list zůstane nezměněn.

Map[f,  {a,  b,  c}]
Map[Sqrt,  {2, 3,  4}]
Sqrt /@ {16, 64, 256}

Apply[f, list] (@@)
Vezme všechny prvky listu a dosadí je jako parametry funkce f. Výsledkem je návratová hodnota funkce f. Původní list zůstane nezměněn.

Apply[f,  {a, b, c}]
Power @@ {x, y, z}
Plus @@ {1, 2, 3}
Times @@ Range[22]   
22!

Timing[1. *(Times @@ Range[50000])] (* faktorial srovnejte s proceduralni implementaci *)

Plus @@ Range[100]

Nest[f, expr, n] 
Opakovaně aplikuje f na expr a to celkem n-krát.

Nest[f, a, 3]

Lambda funkce (lambda, pure, anonymous)

Lambda funkce nám umožňují přirozeně se vyjádřit bez zbytečného psaní a definování speciálních funkcí.
Prosim tebe, můžeš mi to všechno umocnit na druhou ?
nebo
Vezmi první parametr,přičti k němu druhý a celé to umocni na třetí.
Proč ne.

Function[x, x^2];      

    #^2 &;

Function[{x,  y,  z}, (x + y)^z];
(#1 + #2)^#3 &;

A jak se to používá? Jako normální funkce, napíšou se za to hranaté závorky.

Function[x, x^2][a]
Function[x, x^2][11]
#^2 &[11]

Function[x, x^2][a]
Function[{x,  y,  z}, (x + y)^z][a, b, 
      c]
(#1 + #2)^#3 &[1, 2, 3]
#1/(#2 + #3) &[a, b, c]
{-#2 + Sqrt[#2^2 - 4  #1 #3], -#2 - 
              Sqrt[#2^2 - 4  #1 #3]}/(2  #1) &[a, b, 
      c]
{-#2 + Sqrt[#2^2 - 4  #1 #3], -#2 - 
                Sqrt[#2^2 - 4  #1 #3]}/(2.  #1) &[a, b, 
        c] /. {a -> 2}
Solve[%[[1]] == 12, b]
{-#2 + Sqrt[#2^2 - 4  #1 #3], -#2 - 
              Sqrt[#2^2 - 4  #1 #3]}/(2  #1) &[1. , 2, 3]

Lambda funkci snadno aplikujeme na celý list nejlépe pomocí Map nebo Apply

Function[x, x^2] /@ {1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}^2
Apply[(#1  +  #2) (#3  +  #4) &, {1, 2, 3, 4, 
        5}]

Attributes[Power]

vsechno = {1, a, (x + y), {i, j},  
        Pi,  (Sqrt[3])}

    #^2 & /@ vsechno

Příklady

Faktorial pomocí Apply

factfunc[n_] := Times @@ Range[n]
factfunc[22]

Řetězové zlomky pomocí Nest

Clear[x]
Nest[1/(1 + #) &, x, 3]

Programování pomocí pravidel
(Rule based programming)

Vzory (patterns)

Definice

Pattern  je výraz, který může zastoupit celou skupinu výrazů
nejjednodušší pattern je _ neboli Blank

_ je místem ve vzoru, kam se může vložit něco.

? _   

    ?__   

    ?___

_head říká, že to vyhovující něco musí mít hlavičku head.

Head  /@  {a + b, 1, {11, 12}, 1. ,  Cos[x], x -> Pi, 
        Pi}

      

    Cases[{f[1],  f[],  f[x, y],  g[x],  
        1},  _[_Symbol]]

Funkce pro práci s patterny

MatchQ[expr, patt] vrací True, pokud expr vyhovuje patternu patt, jinak vrací False

MatchQ[a + b[] + c, _Plus]
MatchQ[{a, b, c}, _Plus]
MatchQ[{a, b, c}, _List]
MatchQ[f[x], _f]

Cases[expr, pattern] vybere z expr ty případy, kdy pattern platí.
Cases[expr, pattern->rhs] vybere z listu ty případy, kdy pattern platí a provede náhradu.

\(\(\( (*\ 
      najdi\ vsechny\ vyskyty\ neceho\ na\ neco\ v\ prvni\ urovni\ vnoreni\ *) \)\(
\)\(\(expr = 
        1\/4 + c + E\^\(-x\^2\) + Exp[y\^3] + u\^4 + 
          w\^\(5\^x\) + 1\/z\^2;\)

    \(patt = _\^_;\)

    Cases[expr, patt]
 (*\ 
      proc\ to\ funguje\ objasni\ FullForm\ *) 

    FullForm[expr]

    FullForm[patt]\)\)\)

\(\(\( (*\ 
      vyber\ funkci\ f\ s\ jednim\ parametrem\ *) \)\(
\)\(\(Cases[{f[1], \ f[], \ f[x, y], \ g[x], \ 1}, \ 
      f[_]]\)\(\ \)\(
\)\( (*\ 
      vyber\ libovolnou\ funkci\ s\ jednim\ parametrem\ *) \)\(
\)
    \(Cases[{f[1], \ f[], \ f[x, y], \ g[x], \ 
        1}, \ _[_]]\)\(\ \)\(
\)\( (*\ 
      vyber\ libovolnou\ funkci\ se\ dvema\ parametry\ *) \)\(
\)
    \(Cases[{f[1], \ f[], \ f[x, y], \ g[x], \ 
        1}, \ _[_, _]]\)\(\ \)\(
\)\( (*\ 
      vyber\ libovolnou\ funkci\ s\ indexem\ 1\ a\ jednim\ parametrem\ *) \)\(
\)
    \(Cases[{f\_1[1], \ f[], \ f[x, y], \ g\_1[x, y], \ 
        1}, _\_1[_]]\)\(\ \)\)\)\)

Cases[{f[], f[1], f[1, 2], 
        g[1, 2, 3]},  _[_]]
Cases[{f[], f[1], f[1, 2], 
        g[1, 2, 3]},  _[__]]
Cases[{f[], f[1], f[1, 2], g[1, 2, 3]},  
      f[___]]
Cases[{f[], f[1], f[1, 2], g[1, 2, 3]},  
      x_[y__] -> jina[y]]
Cases[{f[], f[x], f[1, 2], g[1, 2, 3]},  
      x_[y_] -> y^x]

Count spočítá vyhovující
Position nalezne posice vyhovujících
DeleteCases vymaže vyhovující

Omezení platnosti patternu

_head říká, že vyhovující výraz musí mít hlavičku head.

Cases[{1, x, 1 + a, 2. },  _Integer]
Cases[{1, x, 1 + a, 2. },  _Symbol]
Cases[{1, x, 1 + a, 2. },  _Plus]

pattern?test 
test je jakákoli funkce jedné proměnné, pokud vrátí True, platnost patternu je potvrzena.

Cases[{1, x, 1 + a, 2. },  _?IntegerQ]
kladne[x_] := x > 0;
Cases[{1, x, 1 + a, 2. },  _?kladne]
Cases[{1, x, 1 + a, 2. },  _?(# > 1 &)]

pattern /; podminka

fl = {f[0], f[1], f[-5], f[Pi], 
          f[2. ]};
Cases[fl, f[x_  /;  Positive[x]]]
Cases[fl, f[x_?Positive]]
Cases[fl, f[x_]  /; x ≥ 0]

Použití nahrazovacích pravidel

Replace (/.) aplikuje pravidlo
ReplaceAll (//.) aplikuje pravidlo tak dlouho, dokud se výraz mění

f[a] + g[b] /. x_[_] -> x
f[a] + g[b] /. _[x_] -> x
FullForm[f[a] + g[b] + c[a, b]]
f[a] + g[b] + c[a, b] /. x_[y___] -> x
%[1, 2]
f[a] + g[b] /. x_[y_] -> y[x]

ClearAll[fibo, n]
Trace[
      fibo[3]  /.  {fibo[1] -> 1,  fibo[2] -> 2, 
          fibo[n_] -> fibo[n - 1] + fibo[n - 2]}]

ClearAll[fibo, n]
fibo[22]  //.  {fibo[1] -> 1,  fibo[2] -> 2, 
        fibo[n_] -> fibo[n - 1] + fibo[n - 2]}

odložené nahrazení
:> pravidlo se vyhodnocuje až v okamžiku použití.

Factor[(a^2) - (b^2)]
Factor[x]
(Sqrt[(a^2) - (b^2)]) /. 
      Sqrt[x_] -> Sqrt[Factor[x]]
(Sqrt[(a^2) - (b^2)]) /. Sqrt[x_] :> Sqrt[Factor[x]]

Podmíněná definice funkce
Funkce můžeme v Mathematice definovat přirozeně, způsobem obvyklým v matematice.

myabs[x_] := x  /;  x ≥ 0
myabs[x_] := -x  /;  x < 0
lst = Table[Random[] -  .5, {i, 20}]
myabs /@ lst

ClearAll[fibo]

      
fibo[n_] := 
        fibo[n - 1] + fibo[n - 2] /; n > 2;
fibo[1. ] = 1;
fibo[2. ] = 2;
fibo[1] = 1;
fibo[2] = 2;

    
fibo[5]
fibo[5. ]
fibo[5.5]
?fibo
DownValues[fibo]
Drop[%, 2]

Chceme-li některou z definic funkce odstranit přiřadíme jí tečku

?fibo
fibo[n_ /; n == 2] =. 
?fibo

Funkce s patterny

Co je ve skutečnosti definice funkce

Funkce je vlastně globální nahrazovací pravidlo
a pokouší-li se Mathematica upravit výraz, uplatňuje na něj postupně všechna pravidla, která zná, tak dlouho, dokud se výsledek mění.

Chceme-li se přesvědčit o tom, jaká pravidla funkci definují, použijeme funkci DownValues[]

ClearAll[fact]
fact[n_Integer] := Times @@ Range[n]
DownValues[fact]

ClearAll[fibo]
fibo[n_] := 1  /; n < 2;
fibo[n_ /; n == 2] := 2;
fibo[n_] := 
        fibo[n - 1] + fibo[n - 2] /; n > 2;
DownValues[fibo]

Vylepšujeme definice naších funkcí

defaultní hodnoty parametrů funkce
k tomu použijeme patterny s defaultními parametry

Cases[{f[x, y, z], f[x, y], f[x], f[]}, 
      f[a_:  1, b_:  2, c_:  3] -> {a, b, c}]

pow2[x_, y_:  2] := 
      Power[x, y]

    pow2[3]

    pow2[3, 3]

    DownValues[pow2]

(* 
      funkce pro prevod cisla v libovolne ciselne soustave (defaultne dvojkove) do soustavy  dekadicke *) 
toInteger[digits_List,  base_:  2] := 
      Fold[base*#1 + #2 &, 0, digits]

    toInteger[{a, b, c}]

    toInteger[{1, 0, 1, 0, 1}]

    toInteger[{1, 0, 1, 0, 1}, 10]

Funkce s volbami

printopts[opts___?OptionQ] := {opts}
printopts[base -> 5, opt2 -> False]

ClearAll[
      toInteger]

    toInteger[digits_List,  opts___?OptionQ] := 
      With[{b = (base /. 
                  Flatten[{opts}] /. {base -> 
                    2})}, 
Fold[b*#1 + #2 &, 0, 
          digits]
]

    toInteger[{a, b, c}, base -> 33]

    toInteger[{1, 0, 1, 0, 1}]

    toInteger[{1, 0, 1, 0, 1}, base -> 10]

příklad: Faktorial

factrule[n_Integer] := n*factrule[n - 1]
factrule[1] := 1
factrule[222]

factrule2[n_Integer] := 
        n*factrule[n - 1]  /; n > 1;
factrule2[n_Integer] = 1  /; n <= 1;
factrule[222]

fact[222] //. {fact[1] -> 1, 
        fact[n_] -> n*fact[n - 1]}

Chytřejší patterny

Pojmenované patterny

Někdy je výhodné pojmenovat si simbolem celý patern, který může být libovolně komplikovaný

{f[a, b], f[a, b, c], f[1, 2, 3, 4]} /. 
      p : f[___] -> Length[p]
  

    {f[a, b], f[a, b, c], f[1, 2, 3, 4]} /. 
      p : f[___] :> Length[p]

Často tímto způsobem zpřehledníme komplikovanou podmínku

Cases[{{a, b}, {a, b, c}, {1, 2, 3, 
          4}}, {___Integer}]
{{a, b}, {a, b, c}, {1, 2, 3, 4}} /. 
      p : {___Integer} :> Plus @@ p
 (* 
      standardnim zpusobem bychom to meli mnohem komplikovanejsi *) 
{{a, b}, {a, b, c}, {1, 2, 3, 4}} /. 
      p_?(VectorQ[#,  IntegerQ] &) :> Plus @@ p

Opakující se patterny

pattern.. jedno a více opakovánípattern, odpovidající výrazy nemusí být stejné
pattern... 0 a více opakovánípattern

Cases[{{1, 1, 1}, {1, 2, 3}, {a, b, c}, {}}, 
      x : {(_Integer) ... } :> 
        f @@ x]
Cases[{{1, 1, 1}, {1, 2, 3}, {a, b, c}, {}}, 
      x : {(z_Integer) ... } :> 
        f @@ x]
  
Cases[{{1, 1, 1}, {1, 2, 3}, {a, b, c}, {}}, 
      x : {___Integer} :> f @@ x]
Cases[{{1, 1, 1}, {1, 2, 3}, {a, b, c}, {}}, 
      x : {z___Integer} :> f @@ x]

Funkce pro kompresi bitmapových obrazků, převádějící sekvenci čísel na dvojice {číslo, počet opakování}. Funkce je význačná tím, že ji takto napsal sám veliký Stephen Wolfram.

ClearAll[runEncode]
runEncode[{rest___Integer,  
            same : (x_Integer) .. }] := 
Append[
          runEncode[{rest}], {x, 
            Length[{same}]}];
runEncode[{}] = {};
runEncode[{1, 1, 1, 1, 2, 2, 2, 3, 1, 1, 2, 2, 5}]

Alternativní patterny

Pokud má pattern odpovídat více možnostem, můžeme tyto možnosti oddělit znakem |

(* 
      vyber pouze listy s realnymi nebo celymi cisly *) 
Cases[{{1. , 2. }, {4, 5. }, {π}, {a, b, c}, {1, 
          2, 3, 4}}, {__Integer | __Real}]

    Cases[{{1. , 2. }, {4, 5. }, {π}, {a, b, c}, {1, 2, 3, 
          4}}, {(_Integer | _Real) .. }]
 (* 
      nasl .  test nemuze platit nikdy,  
      protoze cislo nemuze byt soucasne realne a prirozene *) 

    Cases[{{1. , 2. }, {4, 5. }, {π}, {a, b, c}, {1, 2, 3, 
          4}}, {(x_Integer | x_Real) .. }]

    Cases[{{1. , 2. }, {4, 5. }, {π}, {a, b, c}, {1, 2, 3, 
          4}}, {(x_Integer | y_Real) .. }]
 (* 
      pozor na chybnou syntaxi *) 

    Cases[{{1. , 2. }, {4, 5. }, {π}, {a, b, c}, {1, 2, 3, 
          4}}, {_ (Integer | Real) .. }]

Tipy a triky

Ladění (debugging)

Trace[expr] 
Vygeneruje postup, jak byla expr zpracovávána.

Trace[2 - 3*4]
Trace[Nest[1/(1 + #) &, x, 3]]

TracePrint[expr] 
Vytiskne postup, jak byla expr zpracovávána, řádek po řádku.

TracePrint[2 - 3*4]

TracePrint[Nest[1/(1 + #) &, x, 2]]

TraceDialog[expr] 
Pokaždém kroku zpracování expr se zastaví a zobrazí dialog, kde je možné zkoumat nebo ovlivnit probíhající výpočet, trochu těžkopádné.

TraceDialog[2 - 3*4]

Tipy