閉包是 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 函式,它會回傳一個使用閉包的函式,用來對座標對進行可重複的 2D 平移。
(def move-coordinates-right-2px (translate2d 2 0))
(def result (move-coordinates-right-2px 4 8))
;; result => [6 8]
實作 scale2d 函式,它會回傳一個使用閉包的函式,用來對座標對進行可重複的 2D 縮放。
在這道練習中,假設縮放值都是正數。
(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