4

私は、clojure の遅延シーケンスがいつ遅延するのか、いつ作業が行われるのか、そしてそれらにどのように影響を与えることができるのかを理解しようとしています。

user=> (def lz-seq (map #(do (println "fn call!") (identity %)) (range 4)))
#'user/lz-seq
user=> (let [[a b] lz-seq])
fn call!
fn call!
fn call!
fn call!
nil

ここで「fn call!」が 2 つだけ表示されることを期待していました。それを管理する方法はありますか?とにかく、議論の余地なく 1 つの評価しか必要としないものに移ります。

user=> (def lz-seq (map #(do (println "fn call!") (identity %)) (range 4)))
#'user/lz-seq
user=> (first lz-seq)
fn call!
fn call!
fn call!
fn call!
0

first遅延シーケンスには適していませんか?

user=> (def lz-seq (map #(do (println "fn call!") (identity %)) (range 4)))
#'user/lz-seq
user=> (take 1 lz-seq)
(fn call!
fn call!
fn call!
fn call!
0)

この時点で、すべてを理解することなく、おもちゃの lz-seq の先頭にアクセスする方法について完全に途方に暮れています。どうしたの?

4

4 に答える 4

2

Clojure のシーケンスは遅延型ですが、効率のためにチャンク化され、一度に 32 個の結果のブロックを実現します。

=>(def lz-seq (map #(do (println (str "fn call " %)) (identity %)) (range 100)))
=>(first lz-seq)

fn call 0
fn call 1
...
fn call 31
0

最初に 32 の境界を越えると、同じことが起こります。

=>(nth lz-seq 33)
fn call 0
fn call 1
...
fn call 63
33

実現ごとにかなりの作業を行う必要があるコードの場合、Fogusはチャンキングを回避する方法を提供し、チャンキングを制御する公式の方法が進行中である可能性があることを示唆しています。

于 2012-05-25T08:08:17.883 に答える
2

私は、式がチャンクされたシーケンスを生成すると信じています。範囲式で 4 を 10000 に置き換えてみてください。最初の eval で 32 回の呼び出しのように表示されます。これがチャンクのサイズです。

于 2012-05-25T08:04:32.847 に答える
0

replによって行われたある種の最適化だと思います。私のreplは一度に32をキャッシュしています。

user=> (def lz-seq (map #(do (println "fn call!") (identity %)) (range 100))
#'user/lz-seq
user=> (first lz-seq)
prints 32 times
user=> (take 20 lz-seq)
does not print any "fn call!"
user=> (take 33 lz-seq)
prints 0 to 30, then prints 32 more "fn call!"s followed by 31,32
于 2012-05-25T08:21:07.763 に答える
0

遅延シーケンスは、必要に応じてシーケンスを評価するシーケンスです。(したがって怠け者)。結果が評価されると、再利用できるようにキャッシュされます (再度作業を行う必要はありません)。まだ評価されていないシーケンスの項目を実現しようとすると、clojure はそれを評価し、値を返します。ただし、いくつかの余分な作業も行います。シーケンス内の次の要素を評価したいかもしれないと予想し、それも行います。これは、パフォーマンスのオーバーヘッドを回避するために行われますが、その正確な性質は私のスキル レベルを超えています。したがって、(first lz-seq) と言うと、実際には seq の最初の要素と次のいくつかの要素が計算されます。println ステートメントは副作用であるため、評価が行われていることがわかります。今、あなたが言うなら(2番目のlz-seq)、

シーケンスが遅延していることを確認するより良い方法は次のとおりです。

user=> def lz-seq (map #(do (println "fn call!") (identity %)) (range 400))
#'user/lz-seq
user=> (first lz-seq)

これにより、いくつかの「fn call!」が出力されます。ステートメントですが、400 個すべてではありません。これは、最初の呼び出しが実際にはシーケンスの複数の要素を評価することになるためです。

この説明が十分に明確であることを願っています。

于 2012-05-25T08:08:12.763 に答える