Замикання - це прийом програмування у Clojure, який дозволяє використовувати змінні із зовнішньої лексичної області видимості всередині функції. Clojure підтримує замикання прозоро, і їх часто використовують, навіть не знаючи, що це таке.
;; 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))
Використання atom дає змогу зберегти певний стан:
;; 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))))
Кожен наступний виклик increment збільшує його лічильник на одиницю:
(increment)
;;=> 1
(increment)
;;=> 2
Наша дизайнерська компанія переважно працювала з CSS-перетвореннями для створення вебсторінок. Після деяких обговорень ми вирішили почати використовувати Clojure, щоб виконувати частину обчислень динамічно. Деякі наші колеги мають менший досвід із Clojure, тож ми вирішуємо скористатися замиканням функції, щоб створювати багаторазові перетворення для пар координат {x, y}.
Реалізуйте функцію translate2d, яка повертає функцію, що використовує замикання, щоб виконувати повторюване двовимірне переміщення пари координат.
(def move-coordinates-right-2px (translate2d 2 0))
(def result (move-coordinates-right-2px 4 8))
;; result => [6 8]
Реалізуйте функцію scale2d, яка повертає функцію, що використовує замикання, щоб виконувати повторюване двовимірне масштабування пари координат.
У цій вправі вважайте, що значення масштабування лише додатні.
(def double-scale (scale2d 2 2))
(def result (double-scale 6 -3))
;; result => [12 -6]
Поєднайте дві функції перетворення, щоб виконати повторюване перетворення. Це часто називають композицією функцій, де результат першої функції «f(x)» використовується як вхідні дані для другої функції «g(x)».
(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]
Реалізуйте функцію memoize-transform. Вона приймає функцію, яку треба мемоїзувати, а потім повертає нову функцію, що запамʼятовує вхідні дані переданої функції, тож останнє повернене значення можна «запамʼятати» й обчислити лише один раз, якщо функцію знову викличуть із тими самими аргументами.
Мемоїзацію іноді називають динамічним програмуванням: вона дає змогу виконувати дорогі операції лише один раз, адже їхній результат запамʼятовується.
(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
Зареєструйтеся на Exercism, щоб вивчати й опановувати Clojure, а також 12 концепцій105 вправ та справжнє наставництво від людей, і все це безкоштовно.