Closures sind ein Programmiermuster in Clojure, das es erlaubt, Variablen aus einem äußeren lexikalischen Gültigkeitsbereich innerhalb einer Funktion zu verwenden. Clojure unterstützt Closures transparent, und sie werden oft verwendet, ohne zu wissen, was sie sind.
;; Top-level definitions are global-scope
(def dozen 12)
;; Functions create a new scope.
;; Referencing the outer variable here is a closure.
(fn [n] (* dozen n))
Mit einem Atom lässt sich ein Zustand bewahren:
;; This function closure increments the counter's state
;; in the outer lexical context.
;; This way the counter can be shared between many calling contexts.
(def increment
(let [counter (atom 0)]
(fn [] (swap! counter inc))))
Jeder weitere Aufruf von increment erhöht den zugehörigen Zähler:
(increment)
;;=> 1
(increment)
;;=> 2
Deine Designfirma hat bisher vor allem mit CSS-Transformationen gearbeitet, um Webseiten zu bauen. Nach einiger Diskussion habt ihr beschlossen, Clojure zu verwenden, um einige Berechnungen dynamisch durchzuführen. Einige deiner Teamkollegen haben weniger Erfahrung mit Clojure, deshalb entscheidest du dich, eine Closure zu verwenden, um wiederverwendbare Transformationen für {x, y} Koordinatenpaare zu erstellen.
Implementiere die Funktion translate2d, die eine Funktion zurückgibt, die mithilfe einer Closure eine wiederholbare 2D-Verschiebung eines Koordinatenpaares durchführt.
(def move-coordinates-right-2px (translate2d 2 0))
(def result (move-coordinates-right-2px 4 8))
;; result => [6 8]
Implementiere die Funktion scale2d, die eine Funktion zurückgibt, die mithilfe einer Closure eine wiederholbare 2D-Skalierung eines Koordinatenpaares durchführt.
Gehe bei dieser Übung davon aus, dass Skalierungswerte nur positiv sind.
(def double-scale (scale2d 2 2))
(def result (double-scale 6 -3))
;; result => [12 -6]
Kombiniere zwei Transformationsfunktionen zu einer wiederholbaren Transformation. Das nennt man oft Funktionskomposition: Das Ergebnis der ersten Funktion 'f(x)' wird als Eingabewert für die zweite Funktion 'g(x)' verwendet.
(def move-coordinates-right-2px (translate2d 2 0))
(def double-coordinates (scale2d 2 2))
(def composed-transformations
(compose-transform move-coordinates-right-2px
double-coordinates))
(def result (composed-transformations 0 1))
;; result => [4 2]
Implementiere die Funktion memoize-transform. Sie nimmt eine Funktion entgegen, die memoisiert werden soll, und gibt eine neue Funktion zurück, die sich die Eingabewerte der übergebenen Funktion merkt, sodass der letzte Rückgabewert „erinnert“ werden kann und nur einmal berechnet wird, wenn sie erneut mit denselben Argumenten aufgerufen wird.
Memoisierung wird manchmal auch dynamische Programmierung genannt: Sie erlaubt es, aufwendige Operationen nur einmal auszuführen, da ihr Ergebnis gespeichert wird.
(def triple-scale (scale2d 3 3))
(def memoized-scale (memoize-transform triple-scale))
(memoized-scale 4 3)
;; => [12, 9], this is computed since it hasn't been computed before for the arguments
(memoized-scale 4 3)
;; => [12, 9], this is remembered, since it was computed already
Melde dich bei Exercism an, um Clojure mit 12 Konzepte105 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.