Programiranje

7. predavanje

Proceduralno programiranje - funkcionalno programiranje

Proceduralno programiranje: prati se transformacija podataka od ulaznih podataka do konačnog rezultata.
Funkcionalno programiranje: transformiraju se funkcije tako da transformirane funkcije od polaznih podataka daju konačan rezultat.

U programu Mathematica  "sve je funkcija"

Naredba FullForm ; Head izraza i elementi izraza

Mathematica svaki izraz koji korisnik napiše preformulira u funkcijski izraz tj. sve se prikazuje kao funkcija. Interni prikaz izraza možemo vidjeti pomoću funkcije FullForm.

izraz1=a+b
FullForm[izraz1]

Plus se zove Head izraza (glavni dio izraza), dok su a i b elementi izraza.

Head[izraz1]

izraz2=x*y
FullForm[izraz2]

Times se zove Head (glavni dio) izraza dok su x i y elementi izraza.

Head[izraz2]

FullForm[{1, a, 6, Sin[5]}]

List se zove Head izraza dok su 1 ,  a,  6 i Sin[5] elementi izraza.

Head[{1, a, 6, Sin[5]}]

FullForm[3+Log[4 x+Cos[Pi x]]]

Head[3.14159]

Head[10]

Osnovni elementi funkcionalnog programiranja

Budući svaki izraz Mathematica interno prikazuje kao funkciju koja ima svoje glavni dio (Head) i svoje elemente to je jasno da su osnovne opreacije nad takvim izrazom one koje

mijenjaju glavni dio  izraza - funkcija Apply

mijenjaju elemente izraza - funkcija Map

Promjena glavnog dijela izraza (promjena Head-a)

Sintaksa i semantika

Glavni dio izraza mijenjamo funkcijom Apply. Sintaksa je
            Apply[f, izraz]
a rezultat je novi izraz kojem je glavni dio zamijenjen s f.  
Drugi način pisanja je: 
             f  @@  izraz

Primjeri

lista={a, b, c};
FullForm[lista]

app1=Apply[f,lista];
FullForm[app1]

app2=Apply[Plus, lista]
FullForm[app2]

app3=Apply[Times, lista]
FullForm[app3]

Zbrajanje i množenje članova liste

lista={1, 2, 3, 4};
Apply[Plus, lista]
Apply[Times, lista]

lista2=Table[i^3, {i, 1, 200}];
Apply[Plus, lista2]

lista3=Table[Sin[k*Pi/(2 k+1)], {k,1,20}];
Apply[Times, lista3] // N

Apply s argumentom Plus se može primjeniti na bilo koji Head:

Apply[Plus,g[x,y,z]]

Promijena elemenata izraza

Sintaksa i semantika

Elemente izraza mijenjamo funkcijom Map. Važno: Head izraza se ne mijenja. Način upotrebe je
                 Map[f, izraz]
Rezultat je novi izraz koji ima isti glavni dio kao i polazni i na sve elemente je primjenjena funkcija f.
Drugi način pisanja je: 
             f  /@  izraz

Primjer

lista={a,b,c,d}
FullForm[lista]
mlista=Map[f,lista]
FullForm[mlista]

Map[Sin, Cos[x]]

Primjer

Odredite duljinu svake od podlista liste {{1,2},{3,4,5},{6,7,8,9}}. 

Upotrijebiti ćemo funkciju Length[lista] koja vraća duljinu liste. Točnije, funkcija Length[izraz] vraća broj elementa izraza na koji je primjenjena.

lista={{1,2},{3,4,5},{6,7,8,9}};
FullForm[lista]
mlista=Map[Length, lista]
FullForm[mlista]

Anonimne funkcije (anonymous functions)

Anonimne funkcije se obično definiraja onda kada nam treba funkcija koju ćemo koristiti u tom trenutku i neće nam trebati kasnije.

Ne zadaje se ime takve funkcije.

Funkcija se zadaje samo formulom (bez imena).

Ako se radi o funkciji jedne varijable tada ta varijabla obavezno ima ime #.   Ukoliko se radi o funkcije više varijabli te varijable se zovu #1, #2, #3,...

Kraj formule se, obavezno, označava znakom &.

Primjeri anonimnih fukcija

Uobičajna definicija funkcije koja kvadrira svoj argument je

kvadrat[x_]:=x^2;

i koristimo je na uobičajan način:

kvadrat[7]

Ista funkcija definirana kao anonymous function je (na navodi se ime samo formula) je izraz

#^2&

a upotrebljava se ovako

#^2&[7]

Daljnji primjeri anonymous functions

Funkcija  3x+4(x^3) primjenjena na argument 2:

(3 #+4#^3)&[2]

Funkcija Cos(2x+π) primjenjena na argument  1.2:

Cos[2 # + Pi]&[1.2]

Funkcija ln (3(x^2)+4(y^4)) primjenjena na točku domene (2.1; 3.4):

Log[3 #1^2+4 #2^4]&[2.1, 3.4]

Daljnji primjeri djelovanja na elemente izraza

Primjer

Zadana je lista listi {{0,1,2},{3,4,5},{6,7,8,9}}. Izdvojite druge elemente svake od podlisti.

Kao što je poznato i-ti element liste dobije se pomoću lista[[i]].

Map[#[[2]]&, {{0,1,2},{3,4,5},{6,7,8,9}}]

Primjer

Dodajte nulu na kraj svake od podlisti liste {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}.  
Dodajte nulu na početak svake od podlisti.

Koristiti ćemo funkciju Append[lista, element] koja element dodaje na kraj liste i funkciju Prepend[lista, element] koja element dodaje na početak liste.

lista={{1,2,3},{4,5,6},{7,8,9}};
FullForm[lista]
Map[Append[#, 0]&, lista]
Map[Prepend[#, 0]&, lista]

Primjer

Dodajte nulu na početak i na kraj svako od podlisti liste {{1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11, 12}}.

lista={{1,2,3},{4,5,6,7},{8,9,10,11,12}};
Map[Prepend[Append[#, 0],0]&, lista]

Kombiniranje Apply i Map

Primjer

Napišite izraz koji će davati srednju vrijednost svakog retka matrice.
 
Za ilustraciju ćemo koristiti slučajnu matricu tipa (5, 5) generiranu od brojeva 0 i 1 te ćemo izračunati srednju vrijednost svakog retka.

matrica=Table[RandomInteger[{0,1}] ,{i,1,5},{j,1,5}] 
matrica // MatrixForm
Map[Apply[Plus,#]/Length[#]&, matrica]

Primjer

Za podatke u listi {3.1, 4.2,  5.6, 4.1, 6.7} odredite srednju vrijednost i standardnu devijaciju podataka.

Znamo: srednja vrijednost je (Overscript[x, -])=(1/n)∑(Subscript[x, i]) a standardna devijacija je (Sqrt[(1/n)∑((((Subscript[x, i])-(Overscript[x, -])))^2)])=(Sqrt[(1/n)∑((Subscript[x, i])^2)-((Overscript[x, -])^2)])

lista={3.1,4.2,5.6,4.1,6.7};
n=Length[lista];
xpotez=Apply[Plus, lista]/n
Sqrt[Apply[Plus,Map[#^2&, lista]]/n-xpotez^2]

Iteracije, funkcija Nest

Funkcija Nest

Nest[f, izraz, n]
 	funkcija f se primjeni na zadani izraz n puta.

Primjeri

Nest[f,2 x+y,4]

Nest[(3*Log[#])&,2,3]

Funkcija Framed[izraz] vraća uokvireni izraz.

Framed["Vlado ide u dućan"]

Nest[Framed,"Vlado ide u dućan",6]

U slijedećim primjerima ćemo koristiti činjenicu (bez dokaza) da niz 
              (Subscript[x, i+1])=(1/2)((Subscript[x, i])+(r/(Subscript[x, i]))) 
konvergira prema (Sqrt[r]) za razumno odabrani (Subscript[x, 0]) (Newton).

Koliki je  (Sqrt[2])?

Nest[(# +2/#)/2&, 1.0, 20]

Nest[(# +2/#)/2&, 1.0, 20] // NumberForm[#,16]&

Usporedimo rezultate iteracije s "točnom" vrijednošću.

(Nest[(# +2/#)/2&, 1.0, 2] - Sqrt[2.])// NumberForm[#,16]&

(Nest[(# +2/#)/2&, 1.0, 4] - Sqrt[2.])// NumberForm[#,16]&

(Nest[(# +2/#)/2&, 1.0, 5] - Sqrt[2.])// NumberForm[#,16]&

Koliko je  (Sqrt[4])?

Nest[(# +4/#)/2&, 1.0, 20]

Funkcija NestList

NestList[f, izraz, n]
 	daje listu rezultata kada se funkcija f primjeni na zadani izraz od 0 do n puta. Drugim riječima              dobijemo sve međurezulate funkcije Nest.

Primjeri

NestList[f,2 x+y,4]

NestList[(3*Log[#])&,2,3]

NestList[Framed,"Vlado ide u dućan",6]

Usporedba brzine izvođenja proceduralno i funkcionalno napisanog programa

Brzina izvođenja proceduralnog programa je znatno manja nego brzina ekvivalentnog funkcionalnog programa.

Primjer

Zadatak: provedimo 1,000,000 iteracija po slijedećoj formuli
           (Subscript[x, i+1])=4·(Subscript[x, i])·(1-(Subscript[x, i]))
proceduralno i funkcionalno i izmjerimo vrijeme izvođenja funkcijom Timing. 

Funkcija Timing vraća listu od dva člana; prvi je izmjereno vrijeme a drugi je dobiveni rezultat.

t1=Timing[t=0.37;Do[t=4 t (1-t), {1000000}]; t]

t2=Timing[Nest[4 # (1-#)&, 0.37, 1000000]]

t1[[1]]/t2[[1]]

U ovom primjeru je proceduralno napisan program sporiji približno 15 puta.

Primjer

Zadatak: Od poznate lista realnih brojeva treba dobiti umnožak sinusa elemenata liste.
Za ilustraciju postupka listu ćemo dobiti tako da 300 000 puta generiramo slučajan broj.

lista=Table[RandomReal[],{i,1,300000}];
u=1.;n=Length[lista];

t3=Timing[Do[u=u*Sin[lista[[i]] ], {i,1,n}]];

t4=Timing[Apply[Times,Map[Sin,lista]]];

t3[[1]]/t4[[1]]

U ovom primjeru je proceduralno napisan program sporiji približno 8 puta.