JoeCaml OCaml Konzepte: Grundlegende Konzepte - Die Auswertungsstrategie von OCaml

[weiter]

Die Auswertungsstrategie von OCaml

Auswertung

Eager-Evaluation

Prinzip

Call-By-Value

Reihenfolge

Applicative Order

Zur Auswertung von Ausdrücken verwendet OCaml die Eager-Auswertungsstrategie. Das Prinzip des "Call-By-Value" folgt diesem Ansatz, d.h. alle Argumente einer Funktion werden ausgewertet bevor sie an eine Funktion übergeben werden.
Die Auswertungsreihenfolge ist Applicative Order.

Die Auswertung eines Ausdrucks kann als Transformationsprozess aufgefasst werden, der so lange fortgeführt wird, bis der Ausdruck nicht weiter ausgewertet werden kann.
Dabei sind folgende Regeln zu beachten:

Regeln
  • Konstanten Funktionen/Operationen werten sich zu sich selbst aus
  • Funktionsrümpfe werden erst bei Funktionsapplikation ausgewertet
  • Bei Funktionsapplikation wird jeder Parameter innerhalb des Funktionsrumpfes durch ausgewertetes Argument ersetzt
Ausnahme
# if expr1 then expr2 else expr3;;

Bei einem if-then-else-Konstrukt wird zuerst die Bedingung (expr1) ausgewertet, und dann abhängig von diesem Ergebnis entweder der Ausdruck des Then-Zweigs (expr2) oder des Else-Zweigs (expr3) ausgewertet.

Wird ein Ausdruck ausgewertet, ergeben sich vier Möglichkeiten:


[weiter]

Diese Auswertungsstrategie kann nachteilig sein, da häufig das Ergebnis einer Funktion nicht von allen Argumenten abhängt, jedoch immer alle Argumente ausgewertet werden.

Beispiel:
Nachteil

Funktion 1: zero x = 0

Funktion 2: square x = x * x

Applikation:

zero(square(square(square(2))))
*
*
*
*
zero(256)
0

Die Funktion zero wird, wie durch die oben genannten Auswertungsregeln beschrieben, ausgewertet, indem sukzessiv die Argumente der Funktion ausgewertet werden, obwohl das Ergebnis dieser Auswertungen nicht benötigt wird und die Funktion stets das gleiche Ergebnis liefert.


Zum Problem kann diese Auswertungsstrategie werden bei der Konstruktion eigener Kontrollstrukturen.
Diese werden in funktionalen Programmiersprachen häufig verwendet. Die größte Gefahr dabei besteht in nichtterminierende Auswertungen durch beispielsweise Endlosrekursionen zu laufen.

Beispiel:
Problem

Funktion 1:

cond c e f =
if c then e else f

Funktion 2:

sum n =
if (n = 0)
then n
else (n + sum (n - 1))

Funktion 3:

sum n =
cond (n = 0) n (n + sum (n - 1))

Da, wie durch die Auswertungsstrategie der Eager-Evaluation vorgesehen, mit der Auswertung der Argumente begonnen wird, bevor deren Wert an die Funktion weitergereicht wird, kann das Abbruchkriterium dieser Kontrollstruktur nicht greifen, genauergesagt kommt es erst gar nicht zu dessen Überprüfung.
Der letzte Ausdruck von Funktion 3, also der steuernde rekursive Funktionsaufruf der Funktion selbst, wird ausgewertet, bevor eine Verzweigung in Abhängigkeit des Bedingungstests erfolgen kann.
Dies führt unweigerlich zu einem Stack-Overflow.

Zum Vergleich:

Haskell verwendet die Lazy-Auswertungsstrategie nach dem Prinzip des "Call-By-Need". Hierbei werden die Argumente erst zum Bedarfszeitpunkt ausgewertet. Die Auswertungsreihenfolge dabei ist Normal Order.

OCaml verwendet die Eager-Evaluation zum einen aus Effizienzüberlegungen und der absehbaren Speichernutzung von Programmen, zum anderen wegen den Schwierigkeiten, die sich aus der Möglichkeit der Nutzung von imperativen Konstrukten und Seiteneffekten ergeben.

Es besteht jedoch trotzdem die Möglichkeit, die Vorteile, welche die Lazy-Evaluation bietet, in OCaml zu nutzen.
Durch Einbindung des "Lazy-Moduls", eine der vielen verfügbaren Bibliotheken für Objective Caml, ist Programmierung nach dem Call-By-Need Prinzip umsetzbar.


[ Seminar "Programmierkonzepte und Programmiersprachen" ] [ Gliederung ] [ Grundlegende Konzepte ] [ Variablen in OCaml ]