15
(take 2 (for [x (range 10)
              :let [_ (println x)]
              :when (even? x)] x))
>> (* 0
* 1
* 2
* 3
* 4
* 5
* 6
* 7
* 8
* 9
0 2)

私は私が非常に密集しているだけだと思いました。しかし、いいえ、Clojure は実際には遅延シーケンス (利用可能な場合) の最初の 32 要素を評価することがわかりました。ああ。

for再帰呼び出しがありました:let。計算が深さ優先ではなく幅優先で進行しているように見える理由について、私は非常に興味がありました。再帰ツリーのすべての上位ブランチをたどっていくと、計算 (公平を期すために、メモリではありません) が爆発していたようです。Clojure の 32 チャンクは、コードの論理的な意図が深さ優先であったにもかかわらず、幅優先評価を強制していました。

とにかく、遅延シーケンスの 32 チャンクではなく 1 チャンクを強制する簡単な方法はありますか?

4

2 に答える 2

13

Michaes Fogusは、独自の ISeq 実装を提供することで、この動作を無効にすることに関するブログ エントリを書いています

Colin Jones による修正版から恥知らずに盗むには:

(defn seq1 [#^clojure.lang.ISeq s]
  (reify clojure.lang.ISeq
    (first [_] (.first s))
    (more [_] (seq1 (.more s)))
    (next [_] (let [sn (.next s)] (and sn (seq1 sn))))
    (seq [_] (let [ss (.seq s)] (and ss (seq1 ss))))
    (count [_] (.count s))
    (cons [_ o] (.cons s o))
    (empty [_] (.empty s))
    (equiv [_ o] (.equiv s o))))

The Joy of Clojure には、より簡単なアプローチが示されています。

(defn seq1 [s]
  (lazy-seq
    (when-let [[x] (seq s)]
       (cons x (seq1 (rest s))))))
于 2012-05-11T18:19:59.097 に答える
3

あなたのタイトルの質問に答えるには、いいえ、for怠け者ではありません。 ただし、次のとおりです。

1 つ以上の binding-form/collection-expr のペアのベクトルを取り、その後に 0 個以上の修飾子が続き、expr の評価の遅延シーケンスを生成します。

(私のものを強調)

それで、何が起こっているのですか?

基本的に Clojure は常に厳密に評価します。レイジー seq は基本的に、ジェネレーターなどで python と同じトリックを使用します。

つまり、遅延シーケンスfor を熱心に返します。あなたがそれを要求するまで評価されず、チャンクされます。

于 2012-05-16T00:34:39.587 に答える