11. Partielle Auswertung durch Fold/Unfold

Partielle Auswertung, kann auf funktionalen Sprachen z.B. durch eine Automatisierung der Fold/Unfold-Methode realisiert werden. Hier sollen Programme spezialisiert werden. Dies wird durch eine anfängliche Zielregel verwirklicht, in der einige Variablen zu Konstanten gemacht werden, z.B. f5 x y -> f 5 x y.
Um das spezialisierte Programm zu erzeugen, wird die Programmberechnung simuliert. Für unbekannte Werte werden Variablen benutzt. Reichen Variablen nicht aus, um die Berechnung fortzuführen, werden sie instantiiert. Es wird, wenn möglich, entfaltet, um möglichst viele Programmberechnungen auszuführen, die dann nicht mehr während der Laufzeit durchgeführt werden müssen. Reicht die Information nicht aus, um zu entfalten, wird eine neue Regel erzeugt.
Der vorgestellte Algorithmus verwendet zwei Mengen Pending und Out. Pending enthält dabei noch nicht verarbeitete Regeln (zu Anfang enthält Pending die Zielregel) und Out enthält die Regeln des Zielprogramms.
Es folgt ein Algorithmus zur partiellen Auswertung auf funktionalen Sprachen.

Hauptprogramm :
-
-
-
- -
- -
--


Definition von T
T [|x|] = x
T [|c t1...tn |] = c [|t1|]...T[|tn|]
T[| f t1...tn |] =
- let u1 = T [|t1|],..., un = T[|tn|] in
- let - in
- if -
- if - else
- -
- result is -



Definition von Make_new
Make_new(f t1 ...tn ) = - where
- si = if ti is ground then ti else xi
- -
- g is a new function name, depending only on f and the ground si's
- {i1,...im} = { i | ti is non ground }


T wertet einen Funktionsaufruf so weit wie möglich aus. Reicht die Information nicht aus, um vollständig auszuwerten, wird eine neue Funktion erzeugt. Die Funktion Make_new erzeugt eine neue Funktion ohne konstante Argumente.

Beispiel : Spezialisierung der Ackermannfunktion :

Spezialisiere ack m n auf einen festen Wert von 2 für m
Startregel : ack2 n -> ack 2 n
Originalprogramm :

      ack 0   n	  -> n+1
      ack m+1 0   -> ack m 1
      ack m+1 n+1 -> ack m (ack m+1 n)

Anwendung des obigen Algorithmus ergibt :
Pending = { a2 x -> ack 2 x} (Anfang)
Out = {a2 0 -> 3} (mache x zu , werte komplett aus)
Pending = {a1 y -> ack 1 y ,...} (mache x zu n+1, füge Fkt. a1 hinzu)
Out = {a2 n+1 -> a1 (a2 n),...} ( rufe neue Funktion auf)
Out = {a1 0 ->; 2 ,...} (mache x zu 0, werte komplett aus)
Out = {a1 n+1 -> (a1 n) +1,...} (durch a1 n+1 -> ack 0(ack 1 n ) )


Man kann so sehen, daß man partielle Auswertung durch die Fold/Unfold Methode realisieren kann. Bei dem vorgestellten Algorithmus treten keine Terminierungsverluste auf. Jedoch kann die Terminierung zunehmen, da entfaltet wird, wenn dies bei der 'Call by Value' - Semantik nicht der Fall sein würde. Weiterhin können in diesem Algorithmus Teilberechnungen wiederholt werden.

Weiteres Beispiel :
Spezialisierung des folgenden Programms auf n=5 :
       power 0 x   -> 1
       power n+1 x -> x*power n x

Durch den Algorithmus werden keine weiteren Regeln (außer natürlich der Startregel) zu Pending hinzugefügt. Das neue Programm besteht nur aus einer Regel, die durch Berechnung von power5 x -> T[| x * power 4 x |] entstanden ist.
Das neue Programm lautet :
power5 x -> x * x * x * x * x * 1

Und noch ein Beispiel :
In den folgenden Ausführungen wurden einige Konstruktoren weggelassen. Dadurch sind die beschriebenen Programme zwar nicht ganz korrekt, jedoch besser zu lesen.
Originalprogramm :
append NIL Y -> Y
append zX Y -> z append(X Y)

Eine mögliche Spezialisierung ist :
appendabc Y -> append abc Y

Das entstehende Programm lautet :
appendabc  Y -> abcY ,
da der Term T[|append abc Y|] vollständig berechnet werden kann.
In diesem Beispiel ist sehr gut zu sehen, daß eine Spezialisierung von Y in diesem Programm nichts bringt, da der Programmablauf nur von X beeinflußt wird.
Versucht man trotzdem Y einzufrieren, sieht das Ergebnis wie folgt aus :
Spezialisierungsregel : appendabc X -> append X abc
Durch entsprechende Instantiierungen von X werden beide Programmregeln verarbeitet. Der Algorithmus erzeugt das folgende Programm :
appendabc NIL -> abc
appendabc zX -> z appendabc X

Das Programm hat die gleiche Laufzeit wie das Original. Eine Änderung des Originalprogramms in
 
   append X NIL -> X
   append X Yz -> append(X Y) z

würde bei einer Spezialisierung von X nur konstante Laufzeit haben.

Es ist auch möglich, daß die Spezialisierungsregel auf der rechten Seite eine Verkettung von Funktionen des Originalprogramms enthält. Wichtig ist hierbei nur, daß mindestens eine Variable zu einer Konstanten wird. Betrachte z.B. die Startregel :
app2abc X -> append( abc (append X fgh))

Durch die Parameterauswertung der äußeren Funktion wird der Term (append X abc) ausgewertet. Dabei wird ein Programmteil erzeugt, der der Spezialisierung auf Y im vorigen Beispiel gleicht. Bei der Auswertung der äußeren Funktion kann wieder vollständig ausgewertet werden, wobei wieder das abc vorangestellt wird.