当心 Clojure 的 lazy-seq
jopen
9年前
Clojure 这门语言的优雅之处自然离不开 lazy sequence, clojure.core 核心库里面 很多函数返回值是 lazy-seq(比如 map, filter)。
lazy sequence 的好处
- 先一睹为快, 使用 lazy-cat 定义 Fibonacci 数列。
(def fib-seq (lazy-cat [0 1] (map + fib-seq (rest fib-seq))))
- 结合 map
[x y z ,,,] => [[x 0] [y 1] [z 2] ,,,] ;; (range) 返回无穷整数序列, 如果直接获取他的值, 会内存溢出。 (defn conj-position [xs] (map #(do [%1 %2]) xs (range)))
lazy sequence 的缺陷(不能算是缺陷,是特性)
- 带走异常
;; 定义时候不发生异常, 使用的时候发生异常, 使用的时候一定注意。 ;; 有时会抛出莫名奇妙的错误, 找不到出错点。 user> (def a (for [i (range 10)] (/ 1 i))) ;; => #'user/a user> a ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:156) user>
- binding 上下文环境失效
;; map 返回 lazy sequence, 到使用时才运算, 在这个例子中,计算 ;; vs 的值的时候, 已经脱离了 (binding [*x* 7]) 的环境. user> (def ^:dynamic *x* 3) ;; => #'user/*x* user> user> (def vs (binding [*x* 7] (map #(+ *x* %) (range 10)))) ;; => #'user/vs user> vs ;; => (3 4 5 6 7 8 9 10 11 12) ;; ;; mapv 返回的不是 lazy sequence user> (def vs (binding [*x* 7] (mapv #(+ *x* %) (range 10)))) ;; => #'user/vs user> vs ;; => [7 8 9 10 11 12 13 14 15 16] ;; ;; let 绑定动态 *x* 到 lexical's variable x, 形成闭包。 user> (def vs (binding [*x* 7] (let [x *x*] (map #(+ x %) (range 10))))) ;; => #'user/vs user> vs ;; => (7 8 9 10 11 12 13 14 15 16) user>