Jatkeet TIES341 Funktio ohjelmointi 2 Kevät 2006
Havainto: häntäkutsu (1) Funktiokutsun yleinen toimintaperiaate: (koskee vain täysiä kutsuja, ts. jotka eivät palauta funktiota) kutsuja kirjaa pinoon paluuosoitteen kutsuja kirjaa pinoon parametrit kutsuja hyppää kutsuttavaan kutsuttava tekee työnsä kutsuttava poistaa parametrit pinosta kutsuttava laittaa pinoon paluuarvon kutsuttava katsoo pinosta paluuosoitteen ja hyppää siihen
Havainto: häntäkutsu (2) Tapauksessa f x = g (x+1) f:n kutsuja laittaa paluuosoitteen pinoon f:n kutsuja laittaa argumentin pinoon f:n kutsuja hyppää f:ään f laittaa oman paluuosoitteensa pinoon f laskee x+1 ja laittaa tuloksen pinoon f hyppää g:hen... g tekee mitä tekee, jättää pinoon f:n paluuosoitteen ja tuloksen... f poistaa tuloksen pinosta f poistaa paluuosoitteensa pinosta f poistaa x:n pinosta f palauttaa tuloksen pinoon
Havainto: Häntäkutsu (3) f tekee turhaa työtä! f voisi korvata x:n x+1:llä ja hypätä suoraan g:hen g palatessaan antaa f:n kutsujalle suoraan sen paluuarvon, minkä f olisi välittänyt tätä temppua sanotaan häntäkutsun poistoksi hyödyt: rekursiivinen häntäkutsu (ns. häntärekursio) vaatii vain vakiomäärän pinoa, ei tavallisen rekursion vaatimaa lineaarista määrää vähentää turhaa työtä haittana lähinnä debuggausinformaation häviäminen
Häntäkutsun määritelmä Kutsu on häntäkutsu, jos kutsuva funktio ei tee mitään muuta kutsun päätyttyä kuin palaa omaan kutsujaansa olennaisesti f... = g... g:n kutsu on häntäkutsu f... = g (h...) g:n kutsu on häntäkutsu h:n kutsu ei ole häntäkutsu f... = if... then g... else... g:n kutsu on häntäkutsu jne Poistettu häntäkutsu on olennaisesti parametreja
Häntäkutsun tärkeys Jokainen oikea funktiokieli poistaa kaikki häntäkutsut Mahdollistaa monet funktio ohjelmoinnin idiomit: silmukoiden koodaaminen (häntä)rekursiona äärellisten automaattien idiomi: kukin tila on funktio tilasiirtymä on häntäkutsu
Muunnos häntäkutsuksi, osa 1: akkumulaattoriparametri (1) Ykköskurssilla käytettiin välillä akkumulaattoriparametrin ideaa Suht. yleiskäyttöinen malli, jolla funktiokutsun saa muutettua häntäkutsuksi f x p x = r otherwise = g x (f (h x)) ==> f x = f' r x where f' a x p x = a otherwise = f' (g x a) (h x)
Muunnos häntäkutsuksi, osa 1: akkumulaattoriparametri (2) length [] = 0 length (x:xs) = 1 + length xs ==> length xs = f 0 xs where f a [] = a f a (x:xs) = f (1 + a) xs map f [] = [] map f (x:xs) = f x : xs ==> map' f xs = g [] xs where g a [] = a g a (x:xs) = g (f x : a) xs
Muunnos häntäkutsuksi, osa 1: akkumulaattoriparametri (3) Tämä muunnos ei aina säilytä käyttäytymistä x:t tulevat g:lle eri järjestyksessä ei vaikutusta length esimerkissä map esimerkissä tuloslista on käänteinen pitäisi oikeastaan laittaa reverse g:n kutsun ympärille huom! reverse saadaan id :: [a] > [a] funktiosta tällä muunnoksella :) Muunnos ei ole aina hyödyllinen tilaa säästyy vain jos paluuarvon asymptoottinen tilavaativuus on sublineaarinen rekursion syvyyteen nähden laiskan laskennan vallitessa muutos on joskus vahingollinen map toimii äärettömillä listoilla, map' ei
Muunnos häntäkutsuksi, osa 2: Jatkeenvälitys (1) jatkeenvälitys on englanniksi continuation passing style, CPS g :: C -> B h :: A -> C f :: A -> B f x = g (h x) ==> g' :: C -> (B -> R) -> R h' :: A -> (C -> R) -> R f' :: A -> (B -> R) -> R f' x c = h' x $ \hx -> g' hx c
Idea: Muunnos häntäkutsuksi, osa 2: Jatkeenvälitys (2) funktiolle annetaan parametrina jatke : funktio, joka, kun sitä kutsutaan, tekee sen, mitä alkuperäisen funktion palattua pitää tehdä korvataan arvon palauttaminen funktiokutsulla paluuarvo annetaan tuolle funktiolle parametriksi ==> jokainen kutsu on häntäkutsu Ei ole optimointimuunnos kuten akku Käytetään erilaisissa kirjastoissa mm. Haskellin I/O ennen monadiaikaa Jotkut (funktiokielten) kääntäjät perustuvat tähän ks. Andrew Appel: Compiling with continuations, Cambridge
Call/cc (1) Jotkin kääntäjät tekevät ensi töikseen koko ohjelmalle CPS muunnoksen Tästä syntyi idea: annetaan tämä implisiittinen jatke ohjelmoijalle käyttöön call with current continuation eli call/cc: callcc :: ((a > a) > a) > a callcc (\c >...) suorittaa... :n siten, että c:hen sidotaan funktio, jonka kutsuminen aiheuttaa goton callcc:n kutsujaan siten, että callcc näyttäisi palauttaneen c:lle annetun parametrin Ei löydy Haskellista ei hyödyllinen laiskan laskennan kanssa ja sivuvaikutusten
Call/cc (2) call/cc on erittäin voimakas kontrollirakenne call/cc:llä voidaan toteuttaa poikkeusjärjestelmä korutiinit ohjelmoijan sekoaminen ym ym ym