Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
Haskellprogrammide silumine Alljärgnevalt vaatleme ühte võimalust, kuidas on võimalik vaadelda avaldiste väärtustamisel tekkivaid vahetulemusi. Üheks võimalikuks rakenduseks on näiteks programmidest vigade otsimine, aga samuti aitab vahetulemuste jälgimine paremini aru saada funktsioonide operatsioonilisest käitumisest. > import Observe Vahetulemuste jälgimist võimaldab teostada hugsiga kaasas olevas moodulis Observe defineeritud funktsioon observe, mille tüüp on: observe :: String -> a -> a Funktsionaalselt käitub observe nagu konstantne funktsioon, mis väljastab alati oma teise argumendi. Aga lisaks sellele, kogub ta kokku kõigi antud avaldise väärtustamisel olund teise argumendi väärtused, märgendab need esimese argumendina antud stringiga ning trükib pärast kogu avaldise väärtustamist need vahetulemused välja. Näiteks, kui soovime teada mis on korrutise 2*(3+4) teise teguri väärtus, siis märgendame teisele tegurile vastava alamavaldise funktsiooni observe abil: -- Näide -------------------------------------------------------- Main> 2 * observe "Teine tegur" (3+4) 14 >>>>>>> Observations <<<<<< Teine tegur 7 ----------------------------------------------------------------- Ülaltoodud näites trükkis hugs kõigepealt välja kogu avaldise väärtuse (mis oli 14) ja seejärel funktsiooniga observe märgendatud alamavaldise kõik väärtused. Kuna antud juhul väärtustati seda alamavaldist ainult üks kord, siis ka trükiti välja see ainus väärtus (mis oli 7). Juhul kui märgendatud alamavaldist väärtustatakse mitu korda, siis vastavalt trükitakse välja ka samapalju väärtusi: -- Näide -------------------------------------------------------- Main> sum [observe "Ruudud" (n*n) | n <- [1..4]] 30 >>>>>>> Observations <<<<<< Ruudud 1 4 9 16 ----------------------------------------------------------------- Funktsioon observe ei näita ainult seda, et mis väärtus oli tema teisel argumendil, vaid ka seda kuidas seda väärtust kasutati. Näiteks: -- Näide -------------------------------------------------------- Main> take 3 (observe "List" [1..10]) [1,2,3] >>>>>>> Observations <<<<<< List (1 : 2 : 3 : _) ----------------------------------------------------------------- Kuna ülaltoodud listist tegelikult väärtustati ainult 3 esimest elementi, siis ka funktsioon observe "näeb" ainult neid elemente. Ülejäänud osad väärtusest (so. need, mida tegelikult ei kasutata ja seega ka ei väärtustata) on tähistatud alakriipsuga. Näiteks listi pikkuse leidmisel väärtustatakse ainult lististruktuur, aga mitte elemente. Seetõttu sisalduvad ka alltoodud avaldise vaheväärtuse väljatrükis listielementide kohal alakriipsud: -- Näide -------------------------------------------------------- Main> length (observe "List" (map (*2) [1..10])) 10 >>>>>>> Observations <<<<<< List (_ : _ : _ : _ : _ : _ : _ : _ : _ : _ : []) ----------------------------------------------------------------- Kuna vaheväärtustest trükitakse välja ainult tegelikult kasutatud osad, siis on võimalik ka vaadelda lõpmatuid struktuure (loomulikult ainult sel juhul, kui lõpmatust struktuurist tegelikult kasutatakse ainult lõplikku osa; teisisõnu, kui kogu avaldis termineerub): -- Näide -------------------------------------------------------- Main> takeWhile (<5) (observe "Lõpmatu list" [1..]) [1,2,3,4] >>>>>>> Observations <<<<<< Lõpmatu list (1 : 2 : 3 : 4 : 5 : _) ----------------------------------------------------------------- Vahetulemuste jälgimist on suhteliselt mugav teha, kui väärtustatav avaldis on esitatud funktsioonide kompositsioonioperaatorit kasutades osafunktsioonide jadana. Näiteks: -- Näide -------------------------------------------------------- Main> sum . observe "Ruudud" . map (\x->x*x) $ [1..4] 30 >>>>>>> Observations <<<<<< Ruudud (1 : 4 : 9 : 16 : []) ----------------------------------------------------------------- Funktsioon observe võib esineda avaldises ka mitu korda. Sellisel juhul kogutakse vahetulemused kokku ja väljastatakse eraldi. Näiteks oli laisa-väärtustamise loengus defineeritud funktsioon digits, mis tegi arvu tema kümnendkohtade listiks. Selleks, et näha vahetulemusi kõigi osafunktsioonide järel, lisame vastavatesse kohtadesse erineva märgendiga funktsiooni observe väljakutsed: > digits :: Integer -> [Integer] > digits = reverse > . observe "List pärast map'i" > . map (`mod` 10) > . observe "List pärast takeWhile'i" > . takeWhile (/= 0) > . observe "List pärast iterate'i" > . iterate (`div` 10) -- Näide -------------------------------------------------------- Main> digits 123 [1,2,3] >>>>>>> Observations <<<<<< List pärast map'i (3 : 2 : 1 : []) List pärast takeWhile'i (123 : 12 : 1 : []) List pärast iterate'i (123 : 12 : 1 : 0 : _) ----------------------------------------------------------------- Lisaks "tavalistele" väärtustele on võimalik "vaadelda" ka funktsioone. Sellisel juhul trükitakse välja kõik selle funktsiooni väljakutsete argument-tulemus paarid kasutades Haskelli anonüümsete funktsioonide süntaksit: -- Näide -------------------------------------------------------- Main> observe "sum" sum [1..4] 10 >>>>>>> Observations <<<<<< sum { \ (1 : 2 : 3 : 4 : []) -> 10 } ----------------------------------------------------------------- Loomulikult, kui funktsioon kasutas argumentväärtust ainult osaliselt, siis ka trükitakse välja ainult tegelikult kasutatud osad: -- Näide -------------------------------------------------------- Main> observe "length" length [n*n | n <- [1..4]] 4 >>>>>>> Observations <<<<<< length { \ (_ : _ : _ : _ : []) -> 4 } ----------------------------------------------------------------- Funktsioonide vaatlemine võimaldab jälgida rekursiivselt defineeritud funktsioonide töötamist. Näiteks defineerime faktoriaalifunktsiooni järgenvalt (paneme tahele, et fact ja fact' on vastastikku rekursiivsed): > fact :: Integer -> Integer > fact = observe "fact" fact' > fact' 0 = 1 > fact' n = n * fact (n-1) -- Näide -------------------------------------------------------- Main> fact 5 120 >>>>>>> Observations <<<<<< fact { \ 0 -> 1 , \ 1 -> 1 , \ 2 -> 2 , \ 3 -> 6 , \ 4 -> 24 , \ 5 -> 120 } ----------------------------------------------------------------- Funktsioonide "vaatlemine" võimaldab ka jälgida, kuidas kõrgemat-järku funktsioonid oma funktsioonaalargumente kasutavad. Näiteks alljärgnev demonstreerib kuidas foldr kasutab binaarfunktsiooni listi töötlemisel paremalt vasakule, aga foldl vasakult paremale: -- Näide -------------------------------------------------------- Main> foldr (observe "+" (+)) 0 [1..4] 10 >>>>>>> Observations <<<<<< + { \ 4 0 -> 4 , \ 3 4 -> 7 , \ 2 7 -> 9 , \ 1 9 -> 10 } Main> foldl (observe "+" (+)) 0 [1..4] 10 >>>>>>> Observations <<<<<< + { \ 0 1 -> 1 , \ 1 2 -> 3 , \ 3 3 -> 6 , \ 6 4 -> 10 } ----------------------------------------------------------------- Alljärgnev demonstreerib, et kui binaarfunktsioon on laisk oma teise argumendi suhtes, siis on foldr efektiisem kui foldl: -- Näide -------------------------------------------------------- Main> foldr (observe "&&" (&&)) True [False,True,True,True] False >>>>>>> Observations <<<<<< && { \ False _ -> False } Main> foldl (observe "&&" (&&)) True [False,True,True,True] False >>>>>>> Observations <<<<<< && { \ True False -> False , \ False True -> False , \ False True -> False , \ False True -> False } ----------------------------------------------------------------- Kui vaadeldav funktsioon ise on kõrgemat-järku, siis ka tema funktsionaalargument trükitakse välja "observeeritaval" kujul: -- Näide -------------------------------------------------------- Main> observe "foldl" foldl (+) 0 [1..4] 10 >>>>>>> Observations <<<<<< foldl { \ primPlusInteger 10 [] -> 10 , \ { \ 6 4 -> 10 } 6 (4 : []) -> 10 , \ { \ 3 3 -> 6 } 3 (3 : 4 : []) -> 10 , \ { \ 1 2 -> 3 } 1 (2 : 3 : 4 : []) -> 10 , \ { \ 0 1 -> 1 } 0 (1 : 2 : 3 : 4 : []) -> 10 } -----------------------------------------------------------------