7

私はThe Joy of Clojureをフォローしていましたが、これらの 2 つのステートメントに困惑しています。

(def very-lazy (-> (iterate #(do (print \.) (inc %)) 1) rest rest rest))

(def less-lazy (-> (iterate #(do (print \.) (inc %)) 1) next next next))

したがって、出力は

(println (first very-lazy)) ; .4

(println (first less-lazy)) ; 4

本はそれを説明し続けた

rest で構築された遅延 seq の最初の要素を取得すると、期待どおりに実現されます。しかし、next で構築された seq については、以前に実現されているため、同じことは起こりません。next を使用すると、レイジー seq が 1 つ少ない要素のレイジーになります。これは、実現のコストが高い場合は望ましくない可能性があります。

私の切実な質問は、なぜ「非常に怠け者」に余分なドットがあるのですか? 私の考えでは、「print」は、nextまたはの場合に関係なく、呼び出されたときにその引数を出力しますrest

ありがとう

4

5 に答える 5

7

Print は実際にはどちらの場合もまったく同じことを行い、数値のみを出力しました。エクストラ.はリスト内のコードによって出力されましたが、たまたま の出力と同時に発生した4ため、画面の隣に表示されました。

余分なドットは、その場で作成される遅延シーケンスの副作用です。これを明確にするために、より詳細な例を提案します。

2 つの同一のリストから始めます。どちらも完全に遅延型です。

esc.core=> (def a (iterate #(do (print "making new element") (inc %)) 1)) 
#'esc.core/a
esc.core=> (def b (iterate #(do (print "making new element") (inc %)) 1))
#'esc.core/b

次に、それぞれaとの 4 番目の要素で始まる 2 つの同一のリストを作成します。b

esc.core=> (def a-partially-realized (-> a rest rest rest))
making new elementmaking new element#'esc.core/a-partially-realised
esc.core=> (def b-more-fully-realized (-> b next next next))
making new elementmaking new elementmaking new element#'esc.core/b-more-fully-realised
esc.core=> 

の最初の3 つの要素はa-partially-realized事前に計算され
、 の最初の4 つの要素はb-more-fully-realized事前に計算されています。

最初の要素 (元のリストの 4 番目) を読み取ると、a-partially-realizedまだ計算されていないため、計算されていることがわかります。

esc.core=> (print (first a-partially-realized))
making new element4nil

同じことを行うと、b-more-fully-realisedすでに値がキャッシュされているため、すぐに結果が得られます。

esc.core=> (print (first b-more-fully-realized))
4nil
于 2011-11-01T18:47:52.683 に答える
5

あなたは何か重要なものを見逃しています。ドットを数えます:

user> (def very-lazy (-> (iterate #(do (print \.) (inc %)) 1) rest rest rest))
..#'user/very-lazy

user> (def less-lazy (-> (iterate #(do (print \.) (inc %)) 1) next next next))
...#'user/less-lazy

本を読むだけでなく、実際にコードを実行することは常に役に立ちます。

于 2011-11-01T17:48:55.313 に答える
4

Joost が指摘するように、非常に怠惰な人とあまり怠惰でない人を定義すると、魔法が起こります。

要点は、rest は単にシーケンスを返さなければならないということです。そのシーケンスは怠惰です。したがって、 (-> x rest rest rest) は x の 2 つの値を処理してから遅延シーケンスを返す必要があります。したがって、2つのドット。

一方、next は、シーケンスにそれ以上要素がない場合は nil を返さなければならないため、シーケンスが空かどうかを判断するために xa を 3 回評価する必要があります。だから3つの点。

実際に値を要求するときは、残りの怠惰を破棄する必要があります。したがって、残りのドットがもう 1 つ得られます。次のバージョンが完成したので、ドットはもうありません。

于 2011-11-01T21:19:07.017 に答える
1

これまでに答えられていないのは、あなたが説明する違いの中心にあるのは、なぜnextよりも熱心なのかということです. 長さが 2 未満の任意のリストを返します。対照的に、これらのシナリオでは空のリストを返します。これは、1 つの要素のリストを呼び出すと、(たとえば再帰で) 暗黙的にシーケンスの処理が完了し、最初の要素を実現するときが来たと安全に想定できることを示唆しています。これは、最終ドットの早期実現と後期実現を説明しています。restnextnilrestnext

于 2011-11-01T19:34:59.527 に答える
0

next#()呼び出されるとすぐに関数を実行しますrestが、まだ実行する必要がないため実行しません-可能な限り長く待機します-したがって、この例では、別restの関数が呼び出されるか、値が返される必要があるまで、実現/実行は行われません。これは、印刷の場合です。

私も最初の読書でドットを見逃しました。最後の点を実現するrestのに十分な理由が (まだ) ないため、点が 2 つだけあります。rest

于 2015-07-12T23:58:54.717 に答える