Konkatenative Programmierung mit Factor, Teil 3
9. August 2009 – 17:59 von AndreasIn diesem Teil der Factor-Einführungsreihe beschäftigen wir uns mit den Themen Currying, Kombinatoren und Closures.
1. Currying
Im vorherigen Beitrag wurden Quotations dargestellt, welche erst zum Zeitpunkt ihrer Ausführung ihren Daten-Stack abarbeiten. Es ist allerdings auch möglich bereits zur Definition der Quotation Parameter aus dem Stack in die Quotation zu bringen. Das Zauberwort heißt Currying.
Die Curry-Funktion nimmt eine Funktion entgegen und gibt wieder eine Funktion zurück.
Wenn
f: a X b -> c
und
curry(f)=g
dann gilt
g: a->(b->c)
f mappt also zwei Parameter a und b auf c.
g mappt einen Parameter a auf eine Funktion, die einen Parameter b auf c mappt.
curry mappt eine Funktion f auf eine Funktion g.
In Factor wird Currying mit Hilfe des Words curry, wie in Listing 1.1.
! Argument in die Quotation
1 2 [ ] curry
=> 1 [ 2 ]
! Zweimal curry angewendet würde am Ende
! beide Argumente in die Quotation bringen
1 2 [ ] curry curry
=> [ 1 2 ]
Listing 1.1 Currying in Factor
Factor bietet außer dem Word curry auch noch 2curry, 3curry und ncurry. Die Funktionsweise dieser Words erkennt man am besten anhand der Beispiele in Listing 1.2.
1 [ ] curry
=> [ 1 ]
! bringt zwei Elemente in die Quotation
1 2 [ ] 2curry
=> [ 1 2 ]
! bringt drei Elemente in die Quotation
1 2 3 [ ] 3curry
=> [ 1 2 3 ]
! bringt n Elemente (hier 5) in die Quotation
1 2 3 4 5 [ ] 5 ncurry
=> [ 1 2 3 4 5 ]
Listing 1.2 Anwendung der curry-Words
Je nachdem, wie viele Elemente die Quotation auf dem Stack erwartet, können die entsprechenden curry-Words verwendet werden, um die Elemente direkt in die Quotation zu bringen. Dies kann auch bei den vorherigen Beispielen, wie in Listing 1.3 und 1.4 zu sehen, angewendet werden.
[ + sq ] 2curry ;
: add1-to-result-of-quot ( quot — result )
call( — result ) 1 + ;
Listing 1.3 Verwendung von 2curry in einem Beispiel
=> 37
Listing 1.4 Anwendung der neu definierten Words
In Listing 1.3 werden nun mit 2curry die beiden Stack-Elemente x und y direkt in die Quotation gegeben. Beim call der übergebenen Quotation in add1-to-result-of-quot müssen x und y nicht mehr auf dem Parameter-Stack liegen, da diese ja nun schon in der Quotation enthalten sind.
2. Kombinatoren
Kombinatoren sind spezielle Funktionen höherer Ordnung (Higher-Order Functions). Eine Funktion höherer Ordnung ist wiederum eine Funktion, die Funktionen als Argumente entgegennimmt und gegebenenfalls eine Funktion zurückgibt. In Factor gibt es u.a. drei wichtige Typen von Kombinatoren:
- Cleave Combinators: Diese Kombinatoren wenden mehrere Quotations auf einen einzelnen Wert an.
- Spread Combinators: Diese Kombinatoren wenden mehrere Quotations auf mehrere Werte an.
- Apply Combinators: Diese Kombinatoren wenden eine einzige Quotation auf mehrere Werte an.
Als Beispiel wird der Cleave Combinator bi verwendet, welcher zwei Quotations auf einen Wert anwendet. bi hat den Stack-Effekt: ( wert quot1 quot2 — ). Zuerst wendet bi die Quotation quot1 auf wert an. Danach wird quot2 auf wert angewendet. quot1 und quot2 haben beide denselben Parameter-Stack. Sie erwarten beide den Parameter wert auf dem Stack. Das Ergebnis der Quotations kann unterschiedlich sein und ist nicht vorgegeben. Unter bi-Dokumentation sind weitere Informationen zu bi zu finden.
In Listing 2.1 ist die Anwendung von bi zu sehen.
=> 3
4
Listing 2.1 Verwendung des bi-Kombinators
In dem Code-Beispiel wird zuerst [ 1 + ] auf 2 angewendet, wodurch das Ergebnis 3 auf den Stack gelegt wird. Danach wird [ sq ] auf 2 angewendet und die Zahl 4 als zweites Ergebnis auf den Stack gelegt. Informationen zu weiteren Kombinatoren von Factor können aus der Factor-Dokumentation entnommen werden.
3. Closures
Eine Closure ist eine Funktion, die auf Variablen in ihrem Definitionskontext zugreifen kann. Ein kleines Beispiel hierfür ist unter closures-vs-eval zu finden. Factor bietet hierfür Locals. Sie implementieren einen lexikalischen Scope mit der Unterstützung für Closures. Ein neues Word wird dann nicht mit dem bereits bekannten Doppelpunkt : definiert, sondern mit einem doppelten Doppelpunkt ::. Die restliche Notation ist analog zu der eines normalen Words. Der große Unterschied ist, dass die Eingabewerte aber nicht direkt vom Stack geholt und verarbeitet werden, sondern an die Parameter namentlich gebunden werden. Bei folgender Definition sind x und y also Bindungsvariablen, welche die Eingangs-Stack-Werte an sich binden: :: add ( x y — result ). Innerhalb des Funktionsrumpfes müssen nun auch die Variablen namentlich referenziert werden, um auf deren gebundene Werte zugreifen zu können. Die Werte der Bindungen liegen hierbei auf einem versteckten Stack von Factor. Ein Beispiel hierzu kann aus Listing 3.1 entnommen werden. Die Verwendung dieser Words ist aber analog zu den bisher Behandelten.
x y + ; ! Namentliche Referenzierung ist hier notwendig!
Listing 3.1 Verwendung von Locals
Durch Locals ist es möglich, in Quotations namentlich auf Bindungen zu referenzieren. Die Quotation behält dann Definitionskontext der Bindungen. In Listing 3.2 kann man dies an einem Beispiel sehen.
[ x y + ] ;
Listing 3.2 Verwendung von Locals






