闭包是一种 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