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
}
-----------------------------------------------------------------