Inhalt

Funktionale Programmierung mit Haskell - "Lazy Evaluation"


Haskell - "Lazy Evaluation"

Nicht-strikte Auswertung

strikt
  • 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

Seitenanfang
call-by-value
  • 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

strikt
strictness flags

  • 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

  • Die Koordinaten von Vektoren sollen nur Initialisierungszeit ausgewertet werden

  • So kann die Anzahl der Berechnungen zur Laufzeit minimiert werden (zum Beispiel in einer Grafik-Anwendung)

    data SFVect3D = SFVect3f !Float !Float !Float
    newsfvect f g h = SFVect3f f g h

  • Aufgrund dieser Typ-Definition werden die drei Parameter bei Konstruktion eines neuen Vektors ausgewertet.

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.

zurück Seitenanfang weiter