Predavanje 14: Glava 38 do 40

FlipView[{Text[Style[" Branko Urošević, imqfbrankourosevic@gmail.com ",20,"Text",Italic]],Text[Style[" Faculty of Economics, University of Belgrade, NBS and CESIfo Munich",20,"Text", Italic]]}]

TreeForm[f[g[x,y],List[w,q]]]

Apply[m,f[g[x,y],List[w,q],{1}]]//TreeForm

s@@Apply[m,f[g[x,y],List[w,q],{1}]]//TreeForm

38. Dodeljivanje imena izrazima

Kako se pozvati na prethodni output

Kada hoćete nešto da uradite na brzinu u WL, često je korisno koristiti skraćenu oznaku % da se pozovete na rezultat neposredno prethodnog izračunavanja. Na primer:

Range[10]

Ovako možemo da dignemo na kvadrat te elemente:

%^2

%6^2

Međutim to nije posebno bezbedan način za upotrebu tog operatora. Čim obavimo neko drugo izračunavanje

Range[20]

onda ćemo dobiti drugačiji rezultat nego pre toga

%^2

Ali, primetimo da bi sada

%^2

dalo nešto drugo

Kako možemo da dobijemo ponovo onaj stari rezultat?

Možemo koristeći eksplicitnu oznaku za liniju outputa koju želimo da koristimo (treba uvek proveriti koji je odgovarajući broj output linije koji želimo)

%4^2

Vidimo da je vrlo lako pogrešiti na taj način, odnosno uraditi nešto što baš možda nismo hteli. Ali evo par zgodnih primena tog operatora.

RandomReal[{2,4},40]

Mean[%]

Dakle, ako se dogovorimo da tako uvek pozivamo samo jedan korak unazad to i nije opasno. Evo još jednog primera

NestList[(1+(1/(1+#)))&,1,10]

N[%]

Takve stvari često koristimo

Globalno i neposredno dodeljeno ime pomoću operatora Set (to je znak =)

Ako kasnije želite da se pozovete na neki rezultat, često je veoma korisno da mu dodelite neko ime. Na primer, ako kažete thing=Range[10] onda će thing imati u memoriji upravo listu koja odgovara Range[10]: U početku simbol thing nije prethodno definisan. Stoga:

?thing

thing=Range[10]

Kada smo to uradili, vidimo da je thing postala globalna varijabla čija je vrednost jednaka Range[10]

?thing

Sa njom možemo da računamo kakve hoćemo izraze. Na primer:

Sin[thing]

Često je korisno dodeliti ime čak i ukoliko ne želimo da vidimo odmah taj rezultat. Ako želimo da izbegnemo da vidimo rezultat, stavimo tačku i zapetu na kraju izraza ; To je korisno kada je potencijalan output ogroman. Evo primera:

millionlist=Range[1000000];

Total[millionlist]

Total[(millionlist^2)]

Posledice globalnog dodeljivanja imena

Kada dodelite ime nečemu preko operatora =, to ime će ostati dodeljeno u memoriji dok god ga ekplicitno ne izbrišemo ili izmenimo. Dodelimo x vrednost 42:

x=42;

x+3

Primetimo šta dobijamo ako probamo:

?x

Pretpostavimo da želimo da izračunamo prvi izvod funkcije (x^2) po x. To se radi ovako:

D[(x^2),x]

Clear[x]

D[(x^2),x]

Kako da obrišemo ime ili imena iz memorije

To ne radi dobro pošto x nije promenljiva nego zapravo broj 42. Da bi to radilo kako treba potrebno je prvo očistiti iz memorije x pomoću Clear.

Clear[thing,x]

?x

D[(x^2),x]

x+5

Primetimo da je dodela x=42 globalna. To znači da svuda u ovom notebooku (ili bilo kom drugom koji otvorimo dok god imamo ovu sesiju otvorenu), komp će podrazumevati da je x jednak 42 dok god to ne izbrišemo iz memorije ili dok mu ne dodelimo neku drugu vrednost.

U tom slučaju ne možemo da koristimo simbolička izračunavanja kao što je vađenje izvoda i druge strane. Drugim rečima kada napravimo globalnu dodelu vrednosit kao što je  x=42, to je potencijalno veoma bitno pošto ima uticaj gde god se pojavi simbol x.

Stoga je korisno što manje koristiti globalne identifikacije. I kada ih koristimo, treba ih redovno čistiti iz memorije ukoliko nam nisu više potrebne.

Lokalna dodela imena pomoću funkcije Module-korisno!!!

Globalno dodeljivanje imena treba izbegavati koliko je god moguće. Umesto toga možemo da vrednosti dodeljujemo lokalno, recimo pomoću Module. Evo načina da lokalno dodelimo vrednost x i da ona unutar Module bude jednaka Range[10]:

Module[{x=Range[10]},x^2]

Ne moramo da brisemo sta je x.

D[(x^2),x]

Module[{x},x=Range[10];x^2]

; jer necu da vidim sta je x

U okviru Module, prvo imamo listu deklarisanih promenljivih koje koristimo u Module. Možemo a ne moramo da odmah zadamo njihovu vrednost prilikom deklarisanja. Ono što je dobro u vezi sa tim, jeste da van Module x nema dodeljenu vrednost pa može da se koristi za druge stvari:

x

Integrate[x,x]

U okviru Module možete da imate koliko hoćete varijabli. Ovo je validan izraz:

Module[{x,y=2},x=Range[10];x^2+y]

U funkcionalnom stilu programiranja koji koristimo uglavnom radimo sekvence operacija pomoću funkcija koje se primenjuju jedna za drugom. To je veoma moćan i jednostavan stil programiranja, i najbliži načinu na koji ljudski um misli i na koji, recimo, obična matematika radi.

Range[22,40,2]

Module[{x,y},x=Range[10];y=Range[22,40,2];(x^2)+(y^2)]

Module[{x,y},x=Range[10];y=Range[22,40,2];{x,y,(x^2)+(y^2)}]

Module[{x=Range[10],y=Range[22,40,2]},{x,y,(x^2)+(y^2)}]

Kako obaviti više operacija jednu za drugom koristeći globalnu dodelu imena

Ali, možemo da radimo i drugačije. Naime, možemo da dodeljujemo vrednosti varijablama koje ćemo kasnije da koristimo. Ovakav proceduralni stil karakterističan je za niže programske jezike kao što je C, i nije posebno efikasan u slučaju WL.

U duhu proceduralnog programiranja, ako definišemo sekvencu akcija u Wolfram Language možemo da ih razdvajamo sa tačkom i zapetom;. Kada to uradimo na kraju izraza to je kao da smo deklarisali praznu poslednju akciju, pa stoga ne vidimo taj rezultat. 

Izvršite sledeći niz operacija:

x=Range[10];y=x^2;y=y+10000

Naravno, ovde su i x i y globalne varijable. Ako to želimo da izbegnemo možemo isto to da stavimo u Module. Prvo ih očistimo:

Clear[x,y]

Module[{x,y},x=Range[10];y=x^2;y=y+10000]

?x

To isto sa lokalnom dodelom imena pomoću Module

Ovde vršimo isti niz operacija. Kao što vidimo, u Module prvo moramo da deklarišemo prvo imamo listu svih varijabli koje ćemo koristiti. Onda možemo, posle zapete, u telu Module da ih zapravo definišemo i redefinišemo kako god hoćemo!

Module[{x,y},x=Range[10];y=x^2;y=y+10000]

{x,y}

Vrednosti nekih varijabli mogu da budu odmah definisane, a drugih tek kasnije:

Module[{x,y,n=2},x=Range[10];y=x^n;y=y+10000]

Evo još par primera kako radi Module:

Module[{x=Range[10],n=3,c},c=10;(c^2)+x^n]

Module[{x,n,c},x=Range[10];c=10;n=3;(c^2)+x^n]

Q and A

Zašto smo tek sad zvanično učili o davanju imena izrazima? 
Zato što, kao što smo videli, puno toga u Wolfram Language može da se uradi bez toga! 

Da li mogu da dodelim ime bilo čemu?
Da. Grafiku, array, slici, čistoj funkciji, šta god!

Kako se čita x=4?
“x jednako 4”, ili pridruži  “x vrednost 4”.

Kakva treba da su imena?
Najbolje da budete eksplicitni i specifični, pa makar bila i dugačka. Dobro je počinjati sa malim slovom kako ne bismo imali konflikt sa ugrađenim funkcijama. 

% daje prethodni rezultat. A šta je sa rezultatom pre toga?
%% daje pretposlednji rezultat, %%% daje rezultat pre 3 puta, itd. %n daje rezultat na liniji n (označen kao Out[n]). Ipak ne koristite to previše jer lako dođe do konfuzije.

Mogu li da dodelim vrednosti za celu listu odjednom?
Da. x=y=6 dodeljuje istu vrednost 6 kako x tako i y. {x, y}={3, 4} dodeljuje vrednost 3 za x, a vrednost 4 za y. {x, y}={y, x} menja mestima vrednosti za x i y.

{x,y}={3,5};

{x,y}={y,x}

39. Trenutno i odloženo dodeljivanje vrednosti (Immediate and delayed values)

x je lokalna varijabla zato je zelenkasto

{x,Sin[x]}

ovo smo stavili da bismo dobili parove brojeva

Module[{n},n=10;Table[{x,Sin[x]},{x,-n,n,0.1}]];

ListPlot[Module[{n},n=10;Table[{x,Sin[x]},{x,-n,n,0.1}]]]

ListPlot[Module[{n},n=10;korak=0.01;Table[{x,Sin[x]},{x,-n,n,korak}]]]

Trenutna dodela vrednosti (pomoću =)

Ovde uvodimo pojam trenutnog (oznaka =) i kasnijeg dodeljivanja vrednosti (oznaka :=). U slučaju trenutnog dodeljivanja vrednosti =, vrednost se izračuna odmah u trenutku kada se dodeljivanje izvrši, i posle se više ne izračunava ponovo. U slučaju kasnije dodele vrednosti (delayed assignment, ili :=) , izvrši se dodela ali se vrednost ne izračuna odmah. Umesto toga, vrednost se izračuna iz početka svaki put kad se pozovemo na varijablu koji smo tako dodelili vrednost. 

Kao prost primer razmotrimo razliku između value=RandomColor[] i value:=RandomColor[].

value=RandomColor[]

Dobili smo neku boju

{value,value}

Ova boja vise nije slucajna.

Prvo se izračuna desna strana (odnosno nađe se neka slučajna boja), i onda se ta vrednost dodeli promenljivoj value. Gde god posle koristimo value, ona ima sada fiksiranu vrednost.

?value

nijeslcajno=RandomInteger[100]

{nijeslcajno,nijeslcajno}

slucajno:=RandomInteger[100]

{slucajno,slucajno}

Odloženo dodeljivanje vrednosti pomoću :=

Očistimo to

Clear[value]

value:=RandomColor[]

Primetimo da je dodela napravljena

?value

ali u ovom slučaju desna strani nije izračunata. Naime, svaki put kad pozovemo value sada ćemo imati neku drugu vrednost (iznova će se svaki put izračunati vrednost desne strane dodele)

{value,value}

Možete da napravite delayed assignment za circles čak i ako n nije definisano u trenutku kad to uradimo:

circles:=Graphics[Table[Circle[{x,0},x/2],{x,n}]]

n=8;

circles

Trenutna pravila pomoću Rule (->) i odložena pravila pomoću RuleDelayed (odnosno :>)

Primetimo da, analgno, postoje i Rule i RuleDelayed. Rule, ili x - > rhs izračunava vrednost na desnoj strani (rhs) odmah, pa onda tu vrednost dodeljuje u Rule. U slučaju RuleDelayed, x:>rhs, to što imamo sa desne strane se iznova računa svaki put kad želimo pravilo da primenimo. Ovde imamo trenutno pravilo, sa specifičnom vrednošću RandomReal[ ]:

Ukoliko zamenimo x u sledećem izrazu, prvo će se naći neka vrednost za RandomReal[]. Onda će ta vrednost biti primenjena u izrazu sa leve strane. Ali, za razliku od =, Rule je lokalni operator. Stoga, realizacija slučajnog broja u gornjoj liniji nema veze sa realizacijom u donjoj

{x,x,x,x}/.x->RandomReal[]

?x

{x,x,x,x}/.x->RandomReal[]

Sa druge strane, ukoliko imamo RuleDelayed,  x će se svaki put preračunavati iz početka u istoj liniji:

{x,x,x,x}/.x:>RandomReal[]

: i >

Q and A

Zašto ne koristimo uvek :=?
Zato što ne želite uvek da ponovo izračunavate stvari osim ako nije neophodno. Brže je stvari koje ne treba ponavljati izračunati samo jednom. 

Kako se čitaju  := i :> out loud?
:= se čita“colon equals”, a  :>  “colon greater”.

Šta će se desiti ako stavimo x=x+1, a x nema konkretnu vrednost?
Stvoriće se beskonačna petlja. Isto važi za x={x}. Izbegnite to!

Zašto je važno da su inputi obeleženi sa In[n]:=, a outputi sa Out[n]= ?
To znači da su input dodeljeni In[n] a outputi Out[n]. To što piše := za input znači da je pravi kasnija dodela, to znači da će se menjati svaki put kad nešto promenimo u ćeliji.

U Wolfram Language, proces izračunavanja se zove evaluation (evaluacija) pošto se svodi na to da treba da nađemo vrednosti stvari. 

Wolfram Language ima puno načina da kontroliše evaluaciju, recimo Hold, ili Inactivate. 

Interno, x=y je Set[x, y]. x:=y je SetDelayed[x, y]. x:>y je RuleDelayed[x, y].

40. Korističke funkcije u WL

Uvod u definisanje korisničkih funkcija

((#)^2)&[x]

kvadrat[x_]:=(x^2)

kvadrat[5]

Plot[kvadrat[argument],{argument,-5,5}]

Head[kvadrat[x]]

?kvadrat

kvadrat[{1,2,3,4}]

kvadrat[2+3I]

kvadrat["pera"]

kub[x_]:=(x^3)

kub[{2,5,8}]

Korisnička funkcija je definisana kao naknadna dodela vrednosti (SetDelayed) za neki oblik (pattern). Sa desne strane identifikacije imamo to šta funkcija treba da radi, a sa leve ime funkcije koje je primenjeno na odgovarajući imenovani oblik.

Videli smo da postoji ogroman broj funkcija koji je predefinisan u samom WL. Ali, često je veoma korisno znati kako da definišemo svoje sopstvene funkcije. U WL to je moguće uraditi veoma fleksibilno. Pođimo od prostog primera definicije funkcije.

Ovo definiše funkciju koja pravi tabelu od onoliko pink kvadratića koliko korisnik zahteva:

pinks[n_]:=Table[Pink,n]

pinks[5]

pinks[2.8]

pinks[2+3I]

pinks[-2]

Clear[pinks]

Hocemo da budu samo celi brojevi i onda definisemo  ogranicenje sa desne strane - Integer:

pinks[n_Integer]:=Table[Pink,n]

pinks[2.8]

pinks[2]

pinks[{2,3}]

pinks[-2]

Clear[pinks]

pinks[n_Integer]/;n>0:=Table[Pink,n]

?pinks

pinks[-2]

Vise ne daje gresku

pinks[5]

Kako ta funkcija radi? Ideja je da := definiše vrednost oblika pinks[n_]. Kada tražimo, recimo, pinks[5], to matchuje pinks[n_] pošto je argument n jednak 5.Ta vrednost se izračunava na desnoj strani, tako što se nađe vrednost  Table[Pink,n] za n jednako 5. Ta vrednost se onda pridruži pinks[5]. Ovo nam daje informacije o tome šta je to funkcija pinks

?pinks

Definišimo kvadratnu funkciju bez dodatnih uslova

Definišimo funkciju kvadrat koja diže na kvadrat svaki argument koji joj se da.

kvadrat[x_]:=(x^2)

Primetimo da je imenovani oblik x_ potpuno proizvoljan tako da se pomoću ove funkcije može izračunati kvadrat bilo čega.

{kvadrat[a+b+17],kvadrat[-17]}

Ovako funkciju možemo mapirati na liste na prvom i drugom nivou

Range[10]

Map[kvadrat,Range[10],{1}]

{Range[10],Reverse[Range[10]]}

Map[kvadrat,{Range[10],Reverse[Range[10]]},{2}]

TreeForm[Map[kvadrat,{Range[10],Reverse[Range[10]]},{2}]]

a ovako možemo da je Apply na funkciju f[5 x]

kvadrat@@f[5 x]

Funkciju kvadrat možemo i da predstavimo grafički itd

Plot[kvadrat[a],{a,-3,3}]

Umesto opšte definicije možemo imati različite definicije za različite tipove oblika

Na prethodnom slajdu smo imali opštu definiciju za funkciju kvadrat. Sada ćemo definisati drugačije ponašanje za slučaj kada imamo cele i kada imamo realne brojeve

Clear[kvadrat]

kvadrat[n_Integer]:=((Mod[n,7])^2)

kvadrat[x_Real]:=(x^2)

Znači, ako je argument ceo broj, izračunavamo ostatak od deljenja datog broja sa 7, pa taj ostatak kvadriramo. Ukoliko je argument realan broj, onda samo kvadriramo taj broj. Primenimo ovu funkciju na listu:

kvadrat/@{100,13.2,7,24.3,-11,7/3}

Zašto ovaj poslednji slučaj nije znao da uradi? Pa zato što nismo definisali šta je vrednost funkcije za taj tip brojeva (nismo definisali opštu formulu). Recimo da dodefinišemo:

kvadrat[r_Rational]:=1/(r^2)

kvadrat/@{100,13.2,7,24.3,-11,7/3}

Dakle, za različite tipove vrednosti možemo imati potpuno različite definicije funkcije a isto ime funkcije. To nam daje ogromnu fleksibilnost.

?kvadrat

Mod[100,7]

Sto je manje opste to je veci prioritet kod pisanja funkcija! (pitaj)

Clear[kub]

kub[n_Integer]/;n>0:=(n^3)

uslov pomocu /;

kub[n_Integer]/;n<0:=1/(n^3)

A sta kad je n=0?

kub[0]=0

kub/@Range[-10,10]

Korisnička funkcija može se definisati kroz specijalne slučajeve i opšte izraze

Hoćemo da definišemo funkciju f koja će da radi nešto sa bojama. Pođimo od liste izraza:

Clear[f]

{f[Red],f[Yellow],f[Green],f[Orange],f[Magenta]}

Naravno, pošto nismo rekli šta je f, ništa se ne događa. Definišimo sada vrednosti za f[Red] i f[Green]:

f[Red]=1000;f[Green]=2000;

Sada će uvek f[Red] i f[Green] biti zamenjeni sa vrednostima koje smo definisali; ostale vrednosti nisu definisane (za njih još nismo definisali pravila):

{f[Red],f[Yellow],f[Green],f[Orange],f[Blue]}

{1000,f[],2000,f[],f[]}

Definišimo sada f za proizvoljni argument. Za svaku boju, osim crvene i zelene, f treba da napravi uramljenu kolonu koja se sastoji od date boje (argumenta funkcije) i njenog negativa:

f[x_]:=Framed[Column[{x,ColorNegate[x]}]]

sad ovde ima prioritet boja a ne 1000 i 2000!

f[x_,2]/;n>0:=Framed[Column[{ColorNegate[x],x,ColorNegate[x]}]]

{f[Red,2],f[Yellow,2],f[Green,2],f[Orange,2],f[Blue,2]}

{f[,2],f[,2],f[,2],f[,2],f[,2]}

?f

f[Red,2]=10000

?f

Evo kako ta funkcija radi

Važno istaći da specifična dodeljivanja imaju prednost u odnosu na opštu dodelu. To znači da će, na primer, f[Red] i dalje biti dodeljena vrednost 1000. Međutim, za slučajeve u kojima nismo dodelili specifične vrednosti važi opšta formula:

{f[Red],f[Yellow],f[Green],f[Orange],f[Blue]}

Clear definicije f kako posle ne bismo imali problema. Pre toga pogledajte sledeće:

?f

Trace omogućava da vidimo kako se korak po korak radi unutar programa:

Trace[f[Yellow]]

Clear[f]

Funkcije sa više argumenata

Zamislimo da hoćemo da napišemo funkciju stepen koja dozvoljava korisniku da zada broj koji treba da se stepenuje kao i sam stepen. U tom slučaju:

stepen[x_,n_]:=(x^n)

stepen[121.3,3.14]

Ogranicimo samo na cele brojeve preko Integer

Izračunajmo sedmi stepen brojeva od 10 do 20

stepen[#,7]&/@Range[10,20]

Daje 10 na sedmi, 11 na sedmi itd.

Tu se u prvi argument funkcije, x, ubacuju redom brojevi od 10 do 20 i onda dižu na 7 stepen. Pretpostaivmo da hoćemo da listu brojeva od 10 do 20 dignemo na stepene od 1 do 11 (dve liste moraju biti iste dužine). U tom slučaju možemo da primenimo MapThread

MapThread[stepen,{Range[10,20],Range[1,11]}]

Evo i drugog primera. Ova funkcija izračunava sumu kvadrata dva inputa

sumaKvadrata[x_,y_]:=(x^2)+(y^2)

Plot3D[sumaKvadrata[a,b],{a,-3,3},{b,-3,3}]

D[sumaKvadrata[baba,zaba],baba]

Kako da damo korisniku fleksibilnost da stavi u funkciju br i sa i bez { }

sumakvadrata[{x_,y_}]:=(x^2)+(y^2)

sumakvadrata[x_,y_]:=(x^2)+(y^2)

sumakvadrata[3,4]

sumakvadrata[{3,4}]

sumakvadrata[x:{__}]:=Total[(x^2)]

sumakvadrata[{1,2,3}]

Kako primeniti sumaKvadrata na listu od dva broja

Ova funkcija je definisana sa dva argumenta, a ne pomoću liste od 2 argumenta. Stoga, ovo ne radi:

sumaKvadrata[{3,5}]

Jedan način da iz liste isčupamo dva broja i primenimo ih u sumaKvadrata jeste pomoću Apply:

Apply[sumaKvadrata,{3,5}]

Razmotrimo kako da iz liste parova nađemo samo one čija je suma kvadrata veća od 20. U tom slučaju potrebno je da sumaKvadrata bude Apply (na nivou 0). Koristimo Select

Select[{{1,3},{5,3},{4,4},{2,2},{3,3}},Apply[sumaKvadrata,#]>20&]

Umesto da koristimo Apply, možemo, jednostavno, dodefinisati funkciju sumaKvadrata na sledeći način:

sumaKvadrata[{x_,y_}]:=(x^2)+(y^2)

?sumaKvadrata

sumaKvadrata[{3,5}]

Sada možemo ovako da nađemo tražene elemente liste

Select[{{1,3},{5,3},{4,4},{2,2},{3,3}},sumaKvadrata[#]>20&]

Funkcije sa defoltnim efektom i dodatnim parametrima

Razmotrimo funkciju formatiranje koja u defoltu predstavlja string u fontu 20, u italiku, običnom bojom. Da bismo biti sigurni da će korisnik uneti string, ograničavamo oblika sa _String.

Clear[formatiranje]

formatiranje[unosteksta_String]:=Style[unosteksta,20,Italic]

formatiranje["ovo je proba"]

Sada možemo da dodefinišemo i sledeće. Ovde su “crvena” i “plava” parametri. Parametri mogu biti bilo kakvi validni brojevi, simboli, stringovi itd.

formatiranje[unosteksta_String,"crvena"]:=Style[unosteksta,20,Bold,Red]

formatiranje[unosteksta_String,"plava"]:=Style[unosteksta,20,Underlined,Blue]

Uneli smo dakle i dve druge mogućnosti za korisnika. Da vidimo

formatiranje["ovo je proba","crvena"]

formatiranje["ovo je proba","plava"]

?formatiranje

Cele brojeve možemo formatirati drugačije. Na primer, možemo im dava proizvoljni font od 10 do 30 i proizvoljnu boju

Ispit i kolokvijum!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

formatiranje[broj_Integer]:=Style[broj,RandomInteger[{15,40}],RandomColor[]]

formatiranje[120910]

?formatiranje

Funkcije koje imaju listu kao output

Funkcije mogu da imaju i znatno složenije inpute ili outpute. Na primer, ovde je kako argument funkcije tako i output lista.

plusminus[{x_,y_}]:={x+y,x-y,(x^2)+(y^2),(x^2)-(y^2)}

plusminus[{10,2}]

plusminus[{{10,20},{2,4}}]

plusminus[{a+b,a-n}]

plusminus[{a+b,a-n}]//Expand

To možemo da definišemo i ovako, preko jednog argumenta koji je lista (ovde je, npr, v[[1]] prvi deo liste v)

plusminus[v_List]:={v[[1]]+v[[2]],v[[1]]-v[[2]],((v[[1]])^2)+((v[[2]])^2),((v[[1]])^2)-((v[[2]])^2)}

plusminus[{10,2}]

v={x,y}

v[[1]]

v[[2]]

Input može da bude i samo jedan skalarni parametar, a da output bude komplikovana lista. Na primer, ovde je input proizvoljan ceo broj

cudnaFunkcija[n_Integer]:=Module[{lista=Range[n]},{(lista^2),(lista^3)}]

cudnaFunkcija[5]

Kao rezultat, za zadati ceo broj n dobijamo matricu dimenzija 2xn.

cudnaFunkcija[5]//MatrixForm

cudnaFunkcija[n_Integer]:=Module[{lista},lista=Range[n];{(lista^2),(lista^3)}]

cudnaFunkcija[5]

Funkcije sa uslovima (conditional functions)

Videli smo ranije kako možemo da pišemo uslove u WL. Pomoću njih možemo definisati puno različitih stvari. Recimo, hoćemo funkciju koja bi za zadati celi broj određivala da li je par ili nepar, pa onda parne brojeve stilizovana na jedan način a neparne na drugi.

Clear[parNepar]

VOLI DA DA NA KOLOKVIJUMU I ISPITU!!!!!!!!!!!!

parNepar[n_Integer]:=If[EvenQ[n],Style[n,20,Bold,Red],Style[n,20,Underlined,Blue]]

parNepar/@Range[20]

Možemo da napravimo i nešto kompleksnije: ako je nepar digne se na kub, a ako je par digne se na kvadrat (a formatiranje kao ranije)

parNepar[n_Integer]:=If[EvenQ[n],Style[(n^2),20,Bold,Red],Style[(n^3),20,Underlined,Blue]]

parNepar/@Range[20]

Slično možemo da radimo i sa prostim brojevima. Na primer, ukoliko je broj prost obojimo ga u ljubičasto, a ako nije dignemo ga na kvadrat i obojimo u crveno.

prostIliNe[n_Integer]:=If[PrimeQ[n],Style[n,20,Purple],Style[(n^2),20,Red]]

prostIliNe/@Range[50]

FUNKCIJA KOJA RADI NESTO KAD JE DELJIV SA 6 A NESTO KAD NIJE - PREKO MOD

Npr. vidi kod profesora ne radi mi

If[Mod[n,6]== 0,Style[(n^2),20,Bold,Red],Style[(n^3),20,Underlined,Blue]]

Još primera funkcija sa uslovima

Evo kako možemo da koristimo If funkciju kada radimo sa stringovima. Ova funkcija odgovara na pitanje da li je neka reč ista kao i njoj inverzna reč:

palindromQ[rec_String]:=If[rec==StringReverse[rec],Style[StringJoin[rec," je palindrom!"],20,Bold,Red],Style[StringJoin[rec," nije palindrom!"],20,Underlined,Blue]]

Tu koristimo StringJoin koji spaja dva stringa, string koji proveravamo (rec) i odgovarajući komentar.

palindromQ/@{"ABBA","mama","rever","tata"}

Pretpostavimo sada da imamo više od jednog uslova koji moramo da proverimo pre nego što funkcija nešto treba da uradi. Želimo da nađemo brojeve koji su deljivi sa 2 ali ne i sa 3, brojeve koji su deljivi sa 3 ali ne i sa dva, i najzad, brojeve koji su deljivi kako sa dva tako i sa tri. U zavisnosti od toga, koji slučaj dobijemo bojimo ih u crno, plavo ili crveno. Ako nemamo ni jedan od tih slučajeva, bojimo ih u zeleno. Za to je zgodna funkcija Which. Ona ima sintaksu

While[uslov1, sta uraditi1, uslov 2, sta uraditi 2,....]

Which kada imamo vise uslova!!!!!!!!!

viseUslova[n_Integer]:=Which[Mod[n,2]==0&&Mod[n,3]!=0,Style[n,20,Black],Mod[n,2]!=0&&Mod[n,3]==0,Style[n,20,Blue],Mod[n,2]==0&&Mod[n,3]==0,Style[n,20,Red],Mod[n,2]!=0&&Mod[n,3]!=0,Style[n,20,Green]]

viseUslova/@Range[30]

Još primera sa uslovima

Pretpostavimo da želimo da nateramo funkciju pinks da bude definisana samo za prirodne brojeve veće ili jednake 1, a da ako broj nije takav daje neko uputstvo da ponovo ubacimo broj.

pinks[n_]:=If[IntegerQ[n]&&n>0,Table[Pink,n],Style["Na žalost, broj nije odgovarajući. Moraćete ponovo da probate",20,Red]]

pinks[10]

pinks[-3]

pinks[11.3]

Razmotrimo sada rekurzivnu relaciju za izračunavanje faktorijela. Počnimo od toga da je factorial[1]=1. Onda definišimo rekurzivnu relaciju:

factorial[1]=1;factorial[n_Integer]:=If[n>0,n*factorial[n-1],Style["Input mora biti ceo pozitivan broj",Red]]

factorial[10]

factorial[-5]

Trace[factorial[10]]

10!

Primer rekurzije i dinamičkog programiranja u slučaju brojeva Fibonacci

Pretpostavimo da želimo da izračunamo fibonačijev broj rednog broja n, gde je n pozitivan ceo broj. Treba da iterativno izračunamo sumu prethodna dva broja, ako su početna dva broja jednaka 1:

Moraju se zadati pocetna dva uslova!!!!

fibonaci[1]=1;fibonaci[2]=1;fibonaci[n_Integer]:=If[n>0,fibonaci[n-1]+fibonaci[n-2],"Input mora biti ceo pozitivan broj"]

To radi:

fibonaci[10]

fibonaci[1]=1;fibonaci[2]=1;fibonaci[n_Integer]:=fibonaci[n]=If[n>0,fibonaci[n-1]+fibonaci[n-2],"Input mora biti ceo pozitivan broj"]

Ovo narandzasto smo dodali da bi pamtio prethodne korake.

fibonaci[100]

Ali, ako probamo ovo dole biće previše sporo. To je zato što ovako definisana rekurzija ne pamti to što je prethodno izračunato!

fibonaci[100]

Hajde da to malo promenimo, tako što ćemo rekurziju naterati na pamti prethodno urađene iteracije.

Clear[fibonaci]

fibonaci[1]=1;fibonaci[2]=1;fibonaci[n_Integer]:=fibonaci[n]=If[n>0,fibonaci[n-1]+fibonaci[n-2],"Input mora biti ceo pozitivan broj"]

fibonaci[500]

A evo kako radi predefinisana funkcija. Za n=500 nema razlike u brzini.

Fibonacci[500]

Podsetimo se o čistim funkcijama sa jednom promenljivom

Ukoliko želimo da kreiramo bezimene funkcije, koristimo čiste funkcije. Često su brže od imenovanih funkcija. Puna sintaksa je Function[{x,y,...},body].

((#)^2)&[6]

To je isto što i

Argument x ovde ne mora da se odvaja viticastim zagradama jer je jedini argument.

Function[x,(x^2)][6]

Primetimo da možemo da radimo sve šta hoćemo sa čistim funkcijama. Na primer:

Plot[((#)^2)&[x],{x,-2,2}]

Integrate[((#)^2)&[x],x]

D[((#)^2)&[x],x]

NestList[((#)^2)&,2,10]

cube=((#)^3)+3#&

cube[5]

cube[x]

Kombinacija čistih i imenovanih funkcija

Evo tog istog u punoj formi

Plot[Function[y,(y^2)][x],{x,-2,2}]

Integrate[Function[y,(y^2)][x],x]

D[Function[x,(x^2)][z],z]

Čiste funkcije možemo naknadno da imenujemo. Na primer:

cube=((#)^3)+3 #&

i onda možemo da računamo stvari kao što je prvi izvod:

D[cube[x],x]

Možemo i kombinovati čiste funkcije sa imenovanim. Na primer, napišimo funkciju koja kao argument uzima listu i izdvaja elemente koji su manji od nekog broja n.

ciscenjeListe[ulaznalista_List,n_Integer]:=Select[ulaznalista,#<n&]

RandomReal[{5,15},30]

Izdvajamo brojeve iz liste koji su manji od 10:

ciscenjeListe[RandomReal[{5,15},30],10]

Rešenje algebarske jednačine koristeći NestList i NestWhileList

Pretpostavimo da želimo da rešimo jednačinu g[x] ==0. Kada su u pitanju nelinearne jednačine, Njutn je smislio metod. Naime, potrebno je raditi iteracije sledećeg tipa:

g[(Subscript[x, n])] =(Subscript[x, n-1])-((g[(Subscript[x, n-1])])/(g'[(Subscript[x, n-1])]))

Ovde je prvi korak (Subscript[x, 0]) izabran od strane korisnika, a g'[] je prvi izvod funkcije g. Iteracije nastavljamo sve dok ne dobijemo da je vrednost funkcije g dovoljno mala. Pretpostavimo da tražimo rešenje jednačine g==0, gde je:

g[x_]:=x-Sin[x]

Možemo da proverimo kako rade iteracije počevši od tačke 1.0. Jedan način je da fiksiramo broj iteracija pomoću NestList, a drugi da ih radimo dok god je zadovoljen uslov da je g[]>0.000000001 (na primer):

NestList[#-((g[#])/(g'[#]))&,1.0,25]

NestWhileList[#-((g[#])/(g'[#]))&,1.0,g[#]>0.000000001&]

Ovako radi funkcija predefinisana u WL koja se zove FindRoot. Znamo da je jedino rešenje jednačine x - SIn[x]==0 jeste x=0.

FindRoot[x-Sin[x],{x,1}]

(Subscript[x, n])

Možemo i sami napisati sami funkciju koja će raditi slično

Plot[x-Sin[x],{x,-1,1}]

Clear[findRoot]

Evo i funkcije koja radi veoma slično kao i predefinisana funkcija FindRoot.

findRoot[expr_,{var_,init_},eps_:0.0000000000000001]:=Module[{result,f=Function[Evaluate[var],expr]},
result=NestWhile[#-((f[#])/(f'[#]))&,N[init],Abs[f[#]]>eps&];
{var-> result}]

Zadajemo neki algebarski izraz (expr) kao i par {var, init} koji određuje ime promenljive i njenu početnu vrednost. Zadajemo i tačnost. Po defoltu vrednost eps je jednaka eps_:0.0000000000000001 (tome služi taj izraz).

Sa desne strane, result je varijabla koja sadrži rezultat. A f=Function[Evaluate[var],expr] definiše čistu funkciju čiju nulu želimo da nađemo. Recimo, u našem slučaju to bi bila čista funkcija

Clear[f]

f=Function[Evaluate[x],x-Sin[x]]

za koju je, na primer

f[1.]

findRoot[x-Sin[x],{x,1}]

Nastavak

Prva iteracija Njutnog algoritma za tu funkciju bi bila

#-((f[#])/(f'[#]))&[1.]

Evo kako naša funkcija radi po defoltu kao i ukoliko izaberemo neku drugu vrednost za tačnost:

findRoot[x-Sin[x],{x,1}]

findRoot[x-Sin[x],{x,1},0.00000000001]

Ovde koristimo f=Function[Evaluate[var],expr] a ne samo =Function[var,expr]. Na taj način program prepoznaje da je reč o varijabli koju treba izračunati (Evaluate). Ako to ignorišemo prijavljuje nam da to nije baš u redu, ali ipak sve radi. Generalno je dobra ideja koristiti Evaluate na taj način ali očigledno nije obavezno.

findRoot2[expr_,{var_,init_},eps_:0.0000000000000001]:=Module[{result,f=Function[var,expr]},
result=NestWhile[#-((f[#])/(f'[#]))&,N[init],Abs[f[#]]>eps&];
{var-> result}]

findRoot2[x-Sin[x],{x,1}]

findRoot2[x-(x^2),{x,2}]

Funkciju koja izračunava vrednost potencijalnog profita za različite tipova derivatnih ugovora:

Neka su moguće cene akcija

cene=RandomReal[{80,120},30]

Ako imamo pravo i obavezu da kupimo akciju  po ceni od 100 (cena izvršenja), profit od takvog (dugog) forvard ugovora je razlika cene na tržištu i cene izvršenja. Ako je x moguća realizacije cene akcije, profit bi bio x-100. Mogući profiti bi bili, stoga:

(#-100)&/@cene

Ukoliko možemo da izvršimo ugovor samo ako je njegova vrednost veća od nule (to se zove kol opcija ili opcija da kupimo akciju po ceni od 100), onda bi funkcija izvršenja bila Max[x -100,0]. Ovo bi bili mogući profiti kol opcije

Max[#-100,0]&/@cene

Evo i opšte funkcije:

profitSimulacija[ceneAkcije_List,expr_,var_]:=Module[{result,funkcija=Function[Evaluate[var],expr]},result=funkcija[#]&/@ceneAkcije]

profitSimulacija[cene,s-100,s]

profitSimulacija[cene,Max[s-100,0],s]

Ukoliko imamo opciju da prodajemo akciju po ceni od 100 (put opcija), njen profit bi bio zadat funkcijom Max[100-x,0] pa bi vrednost u trenutku mogućeg izvršenja bila:

profitSimulacija[cene,Max[100-s,0],s]

Primetimo da su pravo na kupovinu i pravo na prodaju akcije po fiksnoj ceni takvi da dok jednom ne smrne drugom ne svane.

Čiste funkcije sa više od jednog argumenta

Kada imamo više argumenata kod čistih funkcija oni se, kako smo ranije objasnili, pišu kao #1, #2 itd. Napravimo sada čistu funkcija koja računa maksimum od razlike između drugog i prvog broja, i nule.

Max[#2-#1,0]&[10,12]

Max[#2-#1,0]&[12,10]

Razmotrimo sada veoma uprošćen model za cene akcija i generišimo dve potencijalne vremenske serije za te cene. Obe su generisane kao slučajni realni brojevi od 20 do 100 (30 observacija). MapThread nam omogućava da izračunamo profit (payoff) od takozvane exchange of assets opcije u svakom trenutku vremena. Ta opcija daje pravo vlasniku da prvu akciju zameni za drugu ukoliko je druga vrednija. Druga akcija je vrednija ukoliko je Max[#2-#1,0]& pozitivan broj.

stock1=RandomReal[{20,100},30]

stock2=RandomReal[{20,100},30]

Exchange-of-assets profit dobijamo ovako:

MapThread[Max[#2-#1,0]&,{stock1,stock2}]

Kako to opštiti za proizvoljne profitne funkcije koje zavise od realizacije cena dve različite hartije

Napišimo funkciju koja kao input uzima podatke po mogućim cenama prve i druge akcije, funkciju profita i varijable. To je korisno da bismo videli kako takve stvari možemo da uopštimo u slučaju više promenljivih.

Clear[profitSimulacija2]

profitSimulacija2[ceneAkcije1_List,ceneAkcije2_List,expr_,var_List]:=Module[{result,funkcija=Function[Evaluate[var],expr]},result=MapThread[funkcija[#1,#2]&,{ceneAkcije1,ceneAkcije2}]]

profitSimulacija2[stock1,stock2,Max[s2-s1,0],{s1,s2}]

Ovo je veom zgodno jer jednostavno možemo da probamo druge vrste profitnih funkcija (druge tipove hartija). Ako nas zanima koliki bi bio profit za opciju kada sa umesto druge akcije dobija prva, samo zamenimo mestima

profitSimulacija2[stock1,stock2,Max[s1-s2,0],{s1,s2}]

Postoji bezbroj načina kako možemo da koristimo funkcije u WL. Na sledećem predavanju videćemo još neke načine koji korise oblike (patterne).