Haskell - "Lazy Evaluation"
Nicht-strikte Auswertung
- Ausdrücke werden in Haskell grundsätzlich nicht-strikt ausgewertet
- Der Wert eines Ausdrucks wird ausgerechnet, wenn er das erste mal ben÷tigt wird,
und dann wiederverwendet
- Ein Ausdruck als aktueller Parameter wird erst ausgewertet, wenn
er innerhalb des Funktionsrumpfes benutzt wird ("Call-by-need")
- Durch implizites "Currying" wird die Anzahl auszuwertender Teilausdrcke minimiert
Konsequenz
Die Division durch null verursacht in anderen Programmiersprachen einen
Laufzeitfehler, wenn sie in einer Zuweisung oder einem Funktionsaufruf vorkommt.
- Haskell behandelt die Division solange als Ausdruck ohne Wert, bis sie (z.B durch die
"Show" -Klasse) ausgewertet werden muss.
Beispiel
- Definiert wird ein Datentyp "LVect3d" mit der Konstruktorfunktion "LVect3f".
- Zus„tzlich werden die "Show"-Klasse zur Ausgabe und die "Eq"-Klasse fr Vergleiche instanziert.
data LVect3D = LVect3f {u,v,w::Float}
newvect u v w = LVect3f u v w
instance Show LVect3D
where show n = show(u n)++" "++show(v n)++" "++show(w n)
instance Eq LVect3D
where (==) a b = ((u a)==(u b)&&(v a)==(v b)&&(w a)==(w b))
Erläuterung
- Mit Hilfe der Funktion "newvect" kann ein neuer Ausdruck dieses Typs erzeugt werden.
Main> newvect 1 0 1
1.0 0.0 1.0
- Die "show"-Methode wird links-assoziativ ausgewertet, erst an dritter Stelle steht ein
nicht terminierender Ausdruck:
Main> newvect 1 2 (1/0)
1.0 2.0
Program error: {primDivFloat 1.0 0.0}
- "Lazy": Der Vergleich schl„gt bereits an der ersten Stelle fehl, der Rest braucht nicht ausgewertet werden :
Main> (newvect 1 2 4)==(newvect 2 (2/0) 4)
False
- Hier reicht der Vergleich der ersten Stellen nicht aus, der zweite Vergleich liefert jedoch einen
Fehler :
Main> (newvect 1 2 4)==(newvect 1 (2/0) 4)
Program error: {primDivFloat 2.0 0.0}
Main>
(Ausgabe im HUGS98)
Strikte Auswertung
- Eine strikt ausgewertete Funktion terminiert nicht, wenn sie auf einen nicht terminierenden Ausdruck angewendet wird.
- Die Auswertung von Teilausdrücken muss in Haskell, wenn nötig, erzwungen werden ("enforce strictness")
"seq"-Funktion und "$!"-Operator
- Die Funktion
seq :: a->b->b wertet einen Ausdruck strikt aus.
- Gemäss Rechts-Assoziativität der Typ-Signatur:
seq::a->(b->b)
seq x f x wertet den Ausdruck x in dem Ausdruck f(x) aus...
- Laut dem "Haskell 98 Report" ist sie durch folgende Gleichungen definiert:
seq _|_ b = _|_
seq a b = b, if a /=_|_
- _|_ steht für einen fehlerhaften oder undefinierten Ausdruck
- Als Funktion
seq x f x oder Infix-Operator x `seq` f x
- Auswertung von seq in Pseudo-Code (!!)
seq x f x <=> (seq x) f x
x=_|_ => seq x f(x)=_|_
x!=_|_ => seq x f(x) = f(x)
- Nützlich für geschachtelte Ausdrücke - "$!"
($), ($!) :: (a -> b) -> a -> b
f $! x = x `seq` f x
"Call-by-value" mit Haskell
- Mit Hilfe der vorgestellten Operatoren kann "call-by-value" mit Haskell simuliert werden.
- Ver„nderung des Vektor-Typs:
data SVect3D = SVect3f {u,v,w::Float}
newvect u v w = ((SVect3f $! u) $! v) $! w
instance Show SVect3D
where show n = show(u n)++" "++show(v n)++" "++show(w n)
instance Eq SVect3D
where (==) a b = ((u a)==(u b)&&(v a)==(v b)&&(w a)==(w b))
- Die Instanzen der "Show" und "Eq"-Klasse bleiben unver„ndert
- Die Methode "newvect" wertet die ihr bergebenen Teeilausdrcke in der Konstruktion des
Vektors strikt aus
Main> (newvect 1 2 2)==(newvect 2 (2/0) 1)
Program error: {primDivFloat 2.0 0.0}
Main>
- Aufgrund der erzwungenen strikten Auswertung wird
(2/0) ausgewertet,
obwohl dies fr den Vergleich nicht notwendig ist.
- Aus dem selben Grund schl„gt auch die "show"-Methode komplett fehl, obwohl ein
Fehler erst an dritter Stelle zu erwarten w„re (s.o.)
Main> newvect 1 2 (2/0)
Program error: {primDivFloat 2.0 0.0}
Main>
"Strictness flags"
- Typkonstruktoren werden im Normalfall nicht-strikt ausgewertet
- Mit "strictness flags" kann die Auswertung aktueller Parameter erzwungen werden
- Sie sind nur in
data -Definitionen erlaubt
Beispiel
Konsequenz
- Eine Methode, die einen beliebigen Ausdruck Vektor mit einem zus„tzlichen Kontrollausdruck markiert
tagvect v = (,) $! v
gettag = (snd)
- Fr den strikten Vektortyp SFVect3D:
Main> gettag (tagvect (newvect 1 (2/0) 3) 1)
1
Main> gettag (tagvect (newsfvect 1 (2/0) 3) 1)
Program error: {primDivFloat 2.0 0.0}
Main>
- Fr den LVect3D-Typ kann also auch ein Tag eines fehlerhaften Vektors ausgewertet werden.
- Die Auswertung des SFVect3D-Typs hingegen schl„gt aufgrund der strikten Typkonstruktion fehl.
|