Lazy Evaluation

Lazy Evaluation (bequeme [faule] Auswertung) bezeichnet in der Informatik eine Art der Auswertung von Ausdrücken, bei der das Ergebnis des auszuwertenden Ausdrucks nur so weit berechnet wird, wie es gerade benötigt wird.

Ein Vorteil einer solchen Auswertungsstrategie ist Zeitersparnis, da Funktionsaufrufe ganz vermieden oder zumindest teilweise eingespart werden können. Außerdem gibt Lazy Evaluation dem Programmierer die Möglichkeit, unendliche Datenstrukturen zu verwenden. Ein Nachteil ist die erschwerte Fehlerbereinigung in Programmen, die lazy evaluiert werden. Oft ist nicht auf den ersten Blick nachvollziehbar, ob ein Ausdruck zu einem bestimmten Zeitpunkt bereits ausgewertet wurde. Dies ist insbesondere dann problematisch, wenn Funktionsaufrufe eine Nebenwirkung haben können.

Ein auf logische Ausdrücke eingeschränkter Spezialfall ist die Kurzschlussauswertung, die auch in vielen nicht-lazy-Sprachen wie C oder Java implementiert ist.

Beispiel

Das folgende in Haskell geschriebene Beispiel zeigt eine Anwendung von Lazy Evaluation.

   squares n = (n*n) : squares (n+1)

Die Funktion squares berechnet die unendliche Liste aller Quadratzahlen beginnend bei n. Das Quadrat von 3 ließe sich also durch den Ausdruck   squares 0 !! 3   berechnen – dabei holt   l !! x   das Element an der Position x aus der Liste l. Ohne Lazy Evaluation würde der Aufruf von squares 0 nicht terminieren. Stattdessen werden nur die Teile berechnet, die wirklich benötigt werden. Die interne Auswertung sieht verkürzt wie folgt aus:

squares 0 !! 3
0*0 : squares (0+1) !! 3
squares (0+1) !! 2
(0+1)*(0+1) : squares (0+1+1) !! 2

(0+1+1+1)*(0+1+1+1) : squares (0+1+1+1+1) !! 0
(0+1+1+1)*(0+1+1+1)
9

Auf den ersten Blick scheint die Lazy Evaluation hier Berechnungen mehrfach auszuführen, da auf der rechten Seite der Funktion squares der Parameter n mehrfach verwendet wird und statt eines Werts eine nicht ausgeführte Berechnung eingesetzt wird. Da Haskell eine rein funktionale Sprache ist, gewährleistet die referentielle Transparenz, dass ein Ausdruck immer das gleiche Ergebnis hat. Somit muss jeder Ausdruck auch nur einmal ausgerechnet werden. Dies nutzt Haskell aus, indem intern für n auf der rechten Seite der Funktionsdefinition anstelle von beispielsweise (0+1+1+1) ein Zeiger auf die Berechnung eingesetzt wird. Wird ein Ausdruck an einer Stelle im Programm ausgerechnet, steht das Ergebnis auch an anderen Stellen zur Verfügung.