3

私は最近 Clojure の学習を始めたばかりなので、これが少し初歩的である場合は申し訳ありません。

誰かが私に次の違いを説明してもらえますか:

=> (def a (lazy-cat
            [0]
            (map inc a)
   ))

=> (take 5 a)
(0 1 2 3 4)

=> (def b (lazy-cat
            [0]
            (map #(inc (nth b %)) (range))
   ))

=> (take 5 b)

IndexOutOfBoundsException   clojure.lang.RT.nthFrom (RT.java:773)

2 番目の例は、b の最初の要素を使用して 2 番目の要素を計算し、次に 2 番目の要素を使用して 3 番目の要素を計算するという同じ方法で機能することを期待していました。私の理解では、clojure は b の 3 番目の要素を計算しようとさえせず、2 番目の要素に値を代入して画面に表示するまでは試行しませんでした。

ここの舞台裏で実際に何が起こっているかについて、誰かが説明してくれるとありがたいです。

ありがとう :)

4

2 に答える 2

2

この動作の理由は、最も単純なケースmapの関数実装です。(map f colls)違いを見ます:

user=> (def b (lazy-cat [0] (map (fn [i _] (inc (nth b i))) (range) (range))))
#'user/b
user=> (take 5 b)
(0 1 2 3 4)

少しややこしいですが、何が起こっているのか説明しましょう。では、2 番目の引数mapが動作を変更する理由:

https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L2469

(defn map
  ...
  ([f coll]
   (lazy-seq
    (when-let [s (seq coll)]
      (if (chunked-seq? s)
        (let [c (chunk-first s)
              size (int (count c))
              b (chunk-buffer size)]
          (dotimes [i size]
              (chunk-append b (f (.nth c i))))
              (chunk-cons (chunk b) (map f (chunk-rest s))))
        (cons (f (first s)) (map f (rest s)))))))
  ([f c1 c2]
   (lazy-seq
    (let [s1 (seq c1) s2 (seq c2)]
      (when (and s1 s2)
        (cons (f (first s1) (first s2))
              (map f (rest s1) (rest s2)))))))
...

答え: の最適化の原因chunked-seq

user=> (chunked-seq? (seq (range)))
true

したがって、値は「事前計算」されます。

user=> (def b (lazy-cat [0] (map print (range))))
#'user/b
user=> (take 5 b)
(0123456789101112131415161718192021222324252627282930310 nil nil nil nil)

もちろん、あなたの場合、この「事前計算」は で失敗しIndexOutOfBoundsExceptionます。

于 2013-01-11T18:21:51.350 に答える
0

のソースを見てくださいtake

(defn take
  "Returns a lazy sequence of the first n items in coll, or all items if
  there are fewer than n."
  {:added "1.0"
   :static true}
  [n coll]
  (lazy-seq
   (when (pos? n) 
     (when-let [s (seq coll)]
      (cons (first s) (take (dec n) (rest s)))))))

最初のケースでそれを実行します。範囲外の配列をアドレス指定する機会はありません。

nth2 番目の例では、まだ n 要素に展開されていないシーケンスを呼び出しています。b は、存在しない要素に依存するシーケンスと 0 を連結しようとします。

于 2013-01-11T18:16:40.770 に答える