當心 Clojure 的 lazy-seq

jopen 9年前發布 | 12K 次閱讀 Clojure JavaScript開發


Clojure 這門語言的優雅之處自然離不開 lazy sequence, clojure.core 核心庫里面 很多函數返回值是 lazy-seq(比如 map, filter)。

lazy sequence 的好處

  1. 先一睹為快, 使用 lazy-cat 定義 Fibonacci 數列。
    (def fib-seq (lazy-cat [0 1] (map + fib-seq (rest fib-seq)))) 
  2. 結合 map
    [x y z ,,,] => [[x 0] [y 1] [z 2] ,,,]
    ;; (range) 返回無窮整數序列, 如果直接獲取他的值, 會內存溢出。
    (defn conj-position [xs]
      (map #(do [%1 %2]) xs (range)))

lazy sequence 的缺陷(不能算是缺陷,是特性)

  1. 帶走異常
    ;; 定義時候不發生異常, 使用的時候發生異常, 使用的時候一定注意。
    ;; 有時會拋出莫名奇妙的錯誤, 找不到出錯點。
    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> 

  2. 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> 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!