Tästä lyhyen oppaan kohdasta voit tutustua/palauttaa mieleesi m-tiedostojen perusasiat. Olemme käyttäneet tässäkin oppaassa m-tiedostoon kirjoitettuja komentojonoja jo edellä. Kun siirrymme ohjelmointiin, kirjoitamme funktiota (ohjelmia) funktio-m-tiedostoihin, kuten yllä viitatussa kohdassa esitetään.
Käsittellä "ohjelmointi" tarkoitetaan yleensä kahta asiaa:
% Tiedostossa tervehdys.m oleva koodi kysyy nimesi
% ja vastaa tervehdyksellä ja päivämäärällä.
% Samalla esitellään vähän merkkijono-operointia.
disp('Hei, kuka olet?')
nimi=input(['Kirjoita nimesi hipsukoissa: ',' Esim: ''Hemuli'' '] );
pvm=date;
vastaus=['Hei ',nimi,'. Tänään on ',pvm,' . '];
disp(vastaus)
Merkkijonoista lyhyesti
Merkkijo on vektori,
joka koostuu ASCII-merkeistä. Esim:
>> mjono='Olen merkkijono'.
Merkkijonoja liitetään peräkkäin, kuten vektoreita. Esim:
>> mjono='Olen merkkijono' mjono = Olen merkkijono >> mjono2=[mjono,'. ', mjono,'.'] mjono2 = Olen merkkijono. Olen merkkijono.Jos merkkijonoon halutaan hipsukka, se kahdennetaan.
hypo.m, jonka otsikkorivi voisi olla:
function c=hypo(a,b)(Huom: Ennen editorin avaamista on syytä valita Matlab-istuntoikkunasta polku omaan työhakemistoon.)
% hypo(3,4) % C=hypo((1:10),(10:-1:1)) % hypo(hilb(3),inv(hilb(3)))Tässäkin tapauksessa suurin osa funktiosta on kommentteja, varsinaista koodiahan on tasan yksi lyhyt rivi.
Kun ryhdyt aikaansaannostasi testaamaan,
suorita ensin >> help hypo , testaile sitten ainakin help-esimerkit läpi.
Huom: Anna komento >> which hypo ja myös which hypot . (Tässä syy, miksi en ehdottanut jälkimmäistä nimeä.)
>> f=@(x) exp(sin(x)) >> ezplot(f,[-pi,pi])Huomaa: Funktion ezplot kutsussa f ei ole merkkijono, kuten ennen, vaan "osoitin-tyyppinen olio". (Vanha merkkijonotyylikin toimii, mutta silloin funktion määrittely on tehtävä myös "vanhanaikaisesti", inline:lla.)
>> ezplot(@(x) exp(sin(x)),[-pi,pi])Määrittely
@(x) f(x) vastaa matemaattista merkintää:
x->f(x) , joka itse asiassa on Maple-ohjelmassa otettu vastaavan
määrittelyn syntaksiksi.
Argumentteja voi tietysti olla n kappaletta:
>> g=@(x,y,z) x.^2+y.^2+z.^2
g =
@(x,y,z)x.^2+y.^2+z.^2
Jatketaan funktio-osoittimien parissa.
Varsin näppärää, nokkelaa, opettavaista,havainnollistavaa,
vuorovaikutteista (ja mitä vielä) on useissa tilanteissa kirjoittaa yhdelle riville iteraatioaskel ja siihen
liittyvät muuttujien päivitykset. Askelta voidaan toistaa nuoli-ylös/ENTER-yhdistelmällä. Alkuarvon hylkäys/hyväksymispäätös ja lopetusehto määräytyvät
vuorovaikutteisesti.
Tyypillinen algoritmi on Newtonin menetelmä epälineaarisen yhtälön
ratkaisemiseksi. Menetelmän ominaisuuksiin kuuluu nopea (kvadraattinen)
suppeneminen silloin, kun se toimii. Ongelmatapauksissa menetelmä taas
huitelee äärettömyyden rajamaille tai jää ikuisesti kiertelemään kehää.
Kaikessa lyhykäisyydessään menetelmän impelementointi käy tällä funktiolla:
>> newt1=@(f,df,x) x-f(x)/df(x)Kyseessä on siis yhtälön f(x)=0 juuren haku. Argumentteina on funktio f, f:n derivaatta df ja alkuarvaus x. Testataanpa helpolla esimerkillä. Haetaan sin:n nollakohtaa:
>> f=@sin,df=@cos
f =
@sin
df =
@cos % sin:n derivaatta
>> x0=1.2 % Alkuarvaus
x0 =
1.2000
>> x0=newt1(f,df,x0) % 1. iteraatio
x0 =
-1.3722
>> x0=newt1(f,df,x0) % Nuoli ylös ja ENTER: 2. iteraatio
x0 =
3.5956
>> x0=newt1(f,df,x0) % ...
x0 =
3.1076
>> x0=newt1(f,df,x0)
x0 =
3.1416
>> format long
>> x0=newt1(f,df,x0)
x0 =
3.141592653589793
>> x0=newt1(f,df,x0)
x0 =
3.141592653589793 % Ei muutu Matlabin laskentatarkkuudella,'
% turha jatkaa.
Huom! Voitaisiin tietysti kirjoittaa suoraan:
Jos haluaisimme tutkia lähemmin iteraatiojonon ominaisuuksia, tarvittaisiin vektori, johon jonon alkioita kumuloitaisiin. Sekin hoituu nuolinäppäintekniikalla:
>> x0=1.3;X=[]; % Kumuloidaan vektoriin X, joka alustetaan tyhjäksi.
>> X=[X x0],x0=newt1(@sin,@cos,x0)
X =
1.3000
x0 =
-2.3021
>> X=[X x0],x0=newt1(@sin,@cos,x0)
X =
1.3000 -2.3021
x0 =
-3.4166
>> X=[X x0],x0=newt1(@sin,@cos,x0)
X =
1.3000 -2.3021 -3.4166
x0 =
-3.1344
>> X=[X x0],x0=newt1(@sin,@cos,x0)
X =
1.3000 -2.3021 -3.4166 -3.1344
x0 =
-3.1416
>> X=[X x0],x0=newt1(@sin,@cos,x0)
X =
1.3000 -2.3021 -3.4166 -3.1344 -3.1416
x0 =
-3.1416
Huomattiin, että pieni alkuarvon muutos vei tuloksen pi:n sijasta arvoon -pi.
Jos jatketaan vielä pari kierrosta ja halutaan tutkia iteraatiojon
peräkkäisten termien erotusta, niin mikäs siinä, funktio
>> format long
>> diff(X)'
ans =
-3.602102447967979
-1.114488696328861
0.282146985976172
-0.007148617036663
0.000000121767538
0
Kun suppeneminen pääsee vauhtiin, oikeiden numeroiden lukumäärä likimain kaksinkertaistuu joka askeleella, sitä se kvadraattisuus on.
Lisää nuolinäppäinalgoritmeja nähnemme jatkossa. Samoin eri versioita teemasta
Newtonin menetelmä.
Ohjausrakenteet
>> help if
IF Conditionally execute statements.
The general form of the IF statement is
IF expression
statements
ELSEIF expression
statements
ELSE
statements
END
The statements are executed if the real part of the expression
has all non-zero elements. The ELSE and ELSEIF parts are optional.
Zero or more ELSEIF parts can be used as well as nested IF's.
The expression is usually of the form expr rop expr where
rop is ==, <, >, <=, >=, or ~=.
Example
if I == J
A(I,J) = 2;
elseif abs(I-J) == 1
A(I,J) = -1;
else
A(I,J) = 0;
end
Koodia voit testata kirjoittamalla se m-tiedostoon, antamalla i:lle ja j:lle
arvoja Matlab-istunnossa ja ajamalla m-tiedosto. (Toki koodin voi kirjoittaa
suoraan istuntoonkin, mutta näinkin monen rivin pläjäys on tuskaista
korjailtavaa ja uudelleen ajettavaa.)
>> x=3;y=2;if x < y, pieni=x; else pieni=y, end
pieni =
2
Seuraavan voit leikata/liimata suoraan istuntoon, koska siinä ei ole muuteltavia parametreja. Samalla tulee pieni merkkijonharjoitelma, funktio
num2str on tällaisissa tarpeen. (Nämä ovat usein hyödyllisiä niksejä
kuvien otsikko-ym. teksteissä.) e=exp(1); if 2^e > e^2 disp(['2^e=',num2str(2^e),' siis suurempi,koska ','e^2=',num2str(e^2)]) else disp(['e^2=',num2str(e^2),' siis suurempi,koska ','2^e=',num2str(2^e)]) end
Esimerkki, for ja if:
function [y] = askel(t)
%askel - Heavisiden yksikköaskelfunktio
% t on vektori
% Funktion arvo =1, jos t(k) >=0, muuten 0.
% Esim:
% askel(-10:10)
n=length(t);
y=zeros(1,n);
for k=1:n
if t(k)>=0
y(k)=1;
end
end
Kuten edellä todettiin, usein voidaan ohjausrakenteita välttää
vektoriajattelulla. Tässä on dramaattinen esimerkki:
Yllä oleva funktio voidaan kaikessa lyhykäisyydessään yhtä hyvin
määritellä näin:
>> vaude=@(t) t>=0 ;Testaa ja selvitä, miksi tämä toimii!
doc while antaa mm. tyyppiesimerkin koodista, jolla lasketaan
"kone-epsilon", eli liukulukulaskennan suhteellinen tarkkuusraja. Kyseessä on
pienin positiivinen liukuluku eps, jolla 1+eps > 1.
Jotta laskennassa ei suotta muutettaisi Matlabin omaa eps-muuttujaa (oikeammin
funktiota, joka käyttäjälle näkyy muuttujana), lisään nimen perään suomalaisittain i:n.
epsi = 1;
while (1+epsi) > 1
epsi = epsi/2;
end
epsi = epsi*2
Toinen esimerkki while-rakenteesta:*** tee a name tälle kohdalle lyhyeen ***
Todettakoon, että ääretön ohjelmasilmukka on helppo toteuttaa:
while 1,...,end
Ehtona on aina 1, eli totuusarvo 'true'. Poistumisehto voidaan rakentaa
vaikka if-lauseen ja break-käskyn avulla. (Tai GUI-ohjelmoinnissa
napin painalluksella.)
Newtonin menetelmä kokonaisena ohjelmana:
Ohjelman ydin voisi mennä näin:
while abs(x-xedell) > eps*abs(x) % Testataan suhteellista virhettä.
xedell=x;
x=x-f(x)/df(x);
end
Tehtävä: Kirjoita tämä täydelliseksi ohjelmaksi kommentteineen
m-tiedostoon. Palauta tuloksena koko iteraatiojono X (Vrt. yllä olevaan
rivi-iteraatioon.) Ota valinnaisiksi argumenteiksi toleranssi tol, jonka
oletusarvona on eps, sekä maxiter, jonka oletus voisi olla vaikka 25.
"Projektitehtäviä"
Esim
function maxvirhe=poly1virhe(n) % polyvirhe Virheapproksimaatio lineaarisessa interpolaatiossa % poly1virhe(n) laskee lineaarisessa interpolaatiossa välillä [0,1] % n:ssä tasavälisesti valitussa otospisteessä lasketun % maksimivirheen. % Interpoloitava funktio annetaan alifunktiona f % Muokattu Kirjan [Hig-Hig] poly1err-esimerkistä s. 148 % 19.8.2010 HA f0=f(0);f1=f(1); x=linspace(0,1,n); p=f0*(1-x)+f1*x; plot(x,f(x),x,p,x,p,'o') maxvirhe=max(abs(f(x)-p)); % Alifunktio: function y=f(x) y=x.*sin(x);Koodi on valmiina myös tässä. Ota käyttöön, kokeile, muokkaa.
Toinen, meille jo tuttu tapa olisi ottaa funktio f argumenttilistaan, jolloin otsikkorivi olisi:
function poly1virhe2(f,n)ja kutsu:
Tässä tapauksessa jälkimmäinen tapa näyttäisi yksinkertaisemmalta, mutta
alifunktiossa voi olla useampia kuin 1 rivi ja niitä voi olla useita.
Myös pääfunkion argumenttilista tulee näin lyhyemmäksi.
Sanotaanpa vain, että riippuu tilanteesta, molempia tapoja tarvitaan, jälkimmäistä erityisesti kutsuttaessa valmiita funktioita, joiden argumenttilistaan
sisältyy funktioita. Valmiita kirjastofunktioitahan käyttäjä ei pääse muokkaamaan, joten alifunkioita ei niihin voi kirjoittaa.
Sisäkkäiset ("nested") funktiot
Kts. [Hig-Hig] 10.7 ss. 151-152
Sisäkkäiset funktiot asustavat toistensa sisällä, nimensä mukaisesti.
Niitä käytettäessä kaikki funktiomääritykset on lopetettava
Perusominaisuus:
Sisäkkäisellä funktiolla on pääsy kaikkien sitä ympäröivien funktioiden työtilaan, ts. jos f2 on f1:n sisällä, niin f2 voi käyttää f1:n muuttujia.
function y=eros(f,x,h)
% Erotusosamääräfunktio (fd_deriv Hig-Hig s. 145)
if nargin < 3, h=sqrt(eps); end
% Numeerinen derivointi on häiriöaltis tehtävä, askeleena
% ei voi käyttää pienempää kuin sqrt(eps). (eps on aivan
% liian pieni)
y=(f(x+h)-f(x))/h;
Koodi on valmiina myös tässä.
Testataan aluksi laskemalla sinifunktion derivaattoja, joita verrataan cos-arvoihin:
>> [eros(@sin,1:10)' cos(1:10)']
ans =
0.5403 0.5403
-0.4161 -0.4161
-0.9900 -0.9900
-0.6536 -0.6536
0.2837 0.2837
0.9602 0.9602
0.7539 0.7539
-0.1455 -0.1455
-0.9111 -0.9111
-0.8391 -0.8391
Lyhyellä näyttötarkkuudella (4 numeroa) täsmälleen samat. Tähän asti kaikki hyvin.
No nyt se sisäkkäisen funktion käyttö:
Otetaan tehtäväksi laskea yleisen 1. asteen rationaalifunktion derivaattojen differenssiapproksimaatioita, kun kertoimet a,b,c,d ovat muuteltavia parametreja.
function dy=ratder(x)
% 1. asteen rationaalifunktion derivaatan differenssiapproksimaatio.
% Sisäkkäisfunktioiden valaisemiseksi.
a=1;b=1;c=1;d=-1; % Muuteltavat parametrit
dy=eros(@rat1,x);
function r=rat1(x)
% Määritellään ao. rationaalifunktio
% (Tämä siis pääsee käyttämään yllä asetettuja arvoja a=1,...,d=-1.)
r=(a+b*x)./(c+d*x);
end
end
>> ratder(-2)
ans =
0.2222
>> format rat
>> ratder(-2)
ans =
2/9
>> format short
>> ratder(2:10)'
ans =
2.0000
0.5000
0.2222
0.1250
0.0800
0.0556
0.0408
0.0313
0.0247
(Tarkistettu "Symbolic toolboxilla".)
Työtapa on siis sellainen, että avataan editoriin tiedosto ratder.m, editoidaan riviä "Muuteltavat parametrit" ja kutsutaan (komentoikkunassa tai erillisessä ajotiedostossa).
Tehtäviä: Tähän tehtäviä, joissa
Funktion sisäiset muuttujat ovat aina paikallisia, ellei
Valaistaan globaaleiden käyttöä parilla esimerkillä.
Esim 1:
function y=omafun(x) global A,B y=1./(A+(x-B).^2);Esim 2:
>> global A,B >> A=0.01;B=0.5; % Muutellaan tarpeen mukaan. >> fplot(omafun,[0 1])Eräs hyötykäyttö voisi olla seuraavanlainen: Ajatellaan, että kutsumme kirjastofunktiota ratkaisija, joka puolestaan kutsuu ratkaisussa tarvittavaa funktiota f. Esim.
function y=f(x) global Laskuri Laskuri=Laskuri+1; y=... ;Istunnossa näin:
>> global Laskuri >> Laskuri=0; >> q=ratkaisija(@f,a,b,...) >> Laskuri % Katsotaan, mikä on arvo nyt.Globaalien muuttujien käyttö voidaan yleensä korvata anonyymeillä tai sisäkkäisillä funktioilla ja kirjastofunktioden omilla monipuolisilla mekanismeilla.