Verschachtelte Funktionsausdrücke in JavaScript
- Lesehinweis
- Die Inhalte dieser Lektion werden in den nächsten Lektionen nicht sofort wieder benötigt, sie sind aber zum Verständnis der Bedeutung von Funktionen in JavaScript hilfreich und passen an diese Stelle des Kurses. Falls ein Leser sich aber derzeit noch nicht für die Inhalte dieser Lektion interessiert, kann er sie auch überspringen und später bei Bedarf auf sie zurückkommen.
Der Wert einer Funktion kann eine Funktion sein.
Wir definieren zunächst eine normale Quadratfunktion.
- Auswertung
quadrat = x => x * x
function quadrat()
Nun definieren wir eine weitere Funktion, deren Anwendung auf ein Argument, die Quadratfunktion ergibt.
- Auswertung
f = x => quadrat
function f()
Die Anwendung der Funktion »f« auf ein Argument ergibt nun wieder die Quadratfunktion.
- Auswertung
q = f( 2 )
function quadrat()
Man kann jetzt sehen, daß »q«, also die Anwendung »f( 2 )«, eine Quadratfunktion ist.
- Auswertung
q( 3 )
9
Wir können »f( 2 )« auch direkt, ohne den Umweg über eine zusätzliche Funktion »q« anwenden.
- Auswertung
f( 2 )( 3 )
9
Wir können »f« auch direkt als eine Funktion definieren, die eine Quadrierfunktion ergibt, ohne einen globalen Eintrag »quadrat« zu verwenden.
- Auswertung
f = d => x => x * x
function f()
Die Hintereinanderschreibung zweier Pfeile ist dabei rechtsassoziativ zu verstehen, also wie in der folgenden Auswertung mit Klammern verdeutlicht wurde.
- Auswertung
f = d =>( x => x * x )
function f()
- Auswertung
f( 2 )( 3 )
9
Schließlich können wir eine Funktion, die ein Funktion ergibt auch ganz ohne Definition globaler Einträge auf ein Argument ausdrücken und anwenden.
- Auswertung
( d => x => x * x )( 2 )( 3 )
9
Auch hier zeigen wir noch einmal die verdeutlichende Schreibweise mit Klammern.
- Auswertung
( d =>( x => x * x ))( 2 )( 3 )
9
Mit den hier vorgestellten Möglichkeiten lassen sich Funktionen schreiben, die auf mehrere Argumente angewendet werden können, beispielsweise ein Funktion zum Summieren zweier Zahlen.
- Auswertung
summe =( x => y => x + y )
function summe()
Auch hier zeigen wir noch einmal die verdeutlichende Schreibweise mit Klammern.
- Auswertung
summe =( x =>( y => x + y ))
function summe()
- Auswertung
summe( 2 )( 3 )
5
Die Anwendung der Funktion »summe« auf das Argument »2« ergibt zunächst die Funktion »y => 2 + y«. Deren Anwendung auf das Argument »3« ergibt dann »2 + 3«, also »5«.
Auch der Aufruf läßt sich mit zusätzlichen Klammern noch verdeutlichen. Die Anwendung von Funktionen ist als linksassoziativ.
- Auswertung
( summe( 2 ))( 3 )
5
Wegen der Polymorphie des Plusoperators »+« kann man mit der Funktion »summe« aber genausogut auch Zeichenfolgen verketten.
- Auswertung
summe( "abc" )( "def" )
"abcdef"
Technisch gesehen handelt es sich bei der Funktion »summe« nicht um eine Funktion mit zwei Parametern, sondern um eine Funktion mit einem Parameter, die eine andere Funktion mit einem Parameter ergibt. Wir werden etwas später sehen, wie man Funktionen ausdrücken kann, die wirklich zwei Parameter haben.
Um eine Funktion auszudrücken, die ihr Argument um »1« erhöht, kann man nun »summe( 1 )« verwenden. Wir haben die Funktion »summe« nun „teilweise“ angewendet, also erst einmal nur auf ein Argument – das zweite „fehlt“ noch. »summe( 1 )« bedeutet nun soviel wie »( y => 1 + y )«, dafür vergeben wir den Namen »inc«.
- Auswertung
inc = summe( 1 )
function summe/<()
Durch den Aufruf der Funktion »inc« können wir nun das zweite, bisher noch „fehlende“ Argument nachliefern. »inc( 1 )« bedeutet soviel wie »( 1 + 3 )«.
- Auswertung
inc( 3 )
4
Um eine Funktion auszudrücken, die ihr Argument um »1« vermindert, kann man »summe( -1 )«, also »( y => -1 + y )« verwenden.
- Auswertung
dec = summe( -1 )
function summe/<()
- Auswertung
dec( 3 )
2
Im folgenden Beispiel wird der Parametername »x« auch für die „innere Funktion“ verwendet. Die beiden »x« in »x + x« beziehen sich dann auf den Parameter der inneren Funktion, weil dessen Festlegung »( x )« ihnen näher ist.
- Auswertung
( x => x => x + x )( 10 )( 2 )
4
Da die Verwendung zweier Namen mit unterschiedlicher Bedeutung in enger Nachbarschaft verwirrend sein kann, muß man sie nicht unbedingt in der Praxis einsetzen, aber es ist interessant zu wissen, daß JavaScript sie erlaubt und versteht.
Wir zeigen noch ein anderes Beispiel mit einer Wirkfunktion.
- Auswertung
f = x => ()=> console.log( x )
function f()
»f« ergibt eine parameterlose Funktion, die das Argument von »f« ausgibt.
Wir wenden »f« auf »2« an und erhalten eine parameterlose Funktion, die »2« ausgibt.
- Auswertung
p = f( 2 )
function f/<()
»p« ist nun eine parameterlose Funktion, die »2« ausgibt.
- Auswertung
p()
2
undefined
Wir wenden »f« auf »abc« an und erhalten eine parameterlose Funktion, die »abc« ausgibt.
- Auswertung
q = f( "abc" )
function f/<()
»q« ist nun eine parameterlose Funktion, die »abc« ausgibt.
- Auswertung
q()
abc
undefined
Wir haben mit »f« also eine Funktion definiert, die Funktionen (wie »p« und »q« oben) erzeugen kann!
Bei jedem Aufruf der Funktion »f« wird dabei eine neue Funktion erzeugt. »p« und »q« sind also zwei verschiedene Funktionen.
Jede dieser beiden Funktionen enthält einen eigenen Parameter »x«, bei »p« ist dieser gleich »2« und bei »q« ist er gleich »abc«.
Inkarnationen
Das zuletzt Beschriebene kann man mit Hilfe des Begriffs Inkarnation besser verstehen.
Zunächst wird eine parametrisierte Funktion »f« definiert.
- Auswertung
f = x => ()=> console.log( x )
function f()
Bei Aufruf von »f« mit einem Argument »2« wird nun eine neue Inkarnation der Funktion »f« angelegt. Es handelt sich dabei um eine Kopie der Funktion »f«, in welcher der Parameter den Argumentwert, also »2«, hat.
- Symbolische Darstellung einer Kopie der Funktion f:
x => ()=> console.log( x )
- Symbolische der Besetzung der Kopie mit der Zuordnung x=2:
()=> console.log( 2 )
Diese Kopie wurde oben als »p« bezeichnet (mit »p = f( 2 )«).
Wird nun »q = f( "abc" )« ausgewertet, so wird eine neue Kopie von »f« mit einem neuen Parameter »x« angelegt, und in jener neuen Kopie erhält der Parameter »x« dann den Wert »"abc"«
- Symbolische Darstellung einer weiteren Kopie der Funktion f:
x => ()=> console.log( x )
- Symbolische der Besetzung der neuen Kopie mit der Zuordnung x=2:
()=> console.log( "abc" )
Diese neue Kopie wird dann als »q« bezeichnet (mit »q = f( 2 )«).
»p« und »q« sind also zwei unabhängige Kopien der Funktion »f« (die sogenannten Inkarnationen), die beide einen eigenen Parameter »x« haben, der einmal »2« und einmal »"abc"« als Wert hat.
Neue Funktionen als neue Verzeichnisse
Da eine Funktion auch ein Verzeichnis ist, haben wir mit dem oben Gezeigtem gleichzeitig auch eine Möglichkeit, um bei Bedarf neue Verzeichnisse anzulegen.
Nach »p = f( 2 )« ist »p« ist ein neues Verzeichnis, wie die folgende Verwendung zeigt.
- Auswertung
p.a = 28
28
- Auswertung
p.a
28
Verzeichnisse werden verwendet, um zusammengehörige Daten zu speichern. So kann man beispielsweise ein neues Verzeichnis anlegen, um darin Daten zu einer Person zu sammeln. Die folgende Funktion »f« ergibt bei jedem Aufrufe eine neuen Funktion und damit ein neues Verzeichnis.
- Auswertung
f = () => () => 0
function f()
Nun legen wir ein neues Verzeichnis »v« an und speichern darin Personendaten.
- Auswertung
v = f()
function f/<()
- Auswertung
v.vorname = "Julius"
"Julius"
- Auswertung
v.nachname = "Caesar"
"Caesar"
Wir legen ein weiteres Verzeichnis »w« an und speichern darin Personendaten.
- Auswertung
w = f()
function f/<()
- Auswertung
w.vorname = "William"
"William"
- Auswertung
w.nachname = "Shakespeare"
"Shakespeare"
Später können wir die Daten wieder auslesen.
- Auswertung
v.vorname
"Julius"
- Auswertung
v.nachname
"Caesar"
- Auswertung
w.vorname
"William"
- Auswertung
w.nachname
"Shakespeare"
- Bildliche Darstellung
v w
.-------------------------. .-------------------------.
| vorname: Julius | | vorname: William |
| nachname: Caesar | | nachname: Shakespeare |
'-------------------------' '-------------------------'
Übungsfragen
? Übungsfragen
Welche Werte haben die folgenden Ausdrücke?
- Ausdruck
( x => o => x + o )( 3 )( 4 )
- Ausdruck 1
( a => b => a( b ))( c => 1 + c )( 2 )
- Ausdruck 2 *
( f => f( 2 )+ f( 3 ))( ( x => y => x + y )( 5 ) )