2012-05-28 4 views
6

をStackOverflowの私は、次のように書いた:Lazynessと

(fn r [f xs] 
    (lazy-seq 
    (if (empty? xs) 
    '() 
    (cons (f (first xs)) (r f (rest xs)))))) 

を解決するために4clojure.comの問題#118:マップなどを使用せずにマップを再実装するように要求し、その解決策が通過するhttp://www.4clojure.com/problem/118

テスト(私はそれが正しいかどうかわからない:それは非常に他のソリューションに近いと述べた)。

問題は、それは私が「ラッピング」怠惰-seqのの私のソリューションによって、上記のコードを書いて怠惰でなければならなかったと述べているので...しかし、私はどのように怠惰-seqの作品を理解していません。

ここでは「怠け者」とは何か、それをどのようにテストできるかはわかりません。

私は(type ...)を尋ねるとき、私は、当然、clojure.lang.LazySeqを取得するが、私はそれと私は単に怠惰-seqの「ラッピング」を削除した場合、私が得るとの違いだか分かりません。

今、もちろん、私は削除場合怠惰-seqの私はこれを実行しようとしている理由のstackoverflowを取得

(= [(int 1e6) (int (inc 1e6))] 
    (->> (... inc (range)) 
     (drop (dec 1e6)) 
     (take 2))) 

そうでない場合(つまり:私が代わりに怠惰-seqのラッピングをさせた場合)、それは正常に動作するようです。

だから私は何とか "デバッグ" /それがすべての仕組みを理解しようとしていることを追跡しようとすることにしました。

(defmacro dbg [x] `(let [x# ~x] (println "dbg: " '~x "=" x#) x#)) 

を、マクロDBG内部の作業バージョンを包み、再びそれを実行しようとしました:私は(私はSO IIRCで見つかった)次のマクロを取りました。そして今、kaboom:うまく動作したバージョンは、今もstackoverflowをスローします。

私はよく分かりません。おそらく、評価されないものの評価を強制するマクロの望ましくない効果でしょうか?誰もがこの単純な機能と簡単なテストを使用して、説明することができれば、lazynessはここで仕事をしてどのように、いつ正確に何を呼び出さ素晴らしいことだ

など

+0

Clojureは '実現?レイジーシーケンスの値が実現されているかどうかをテストするテスト関数。例えば(実現?(範囲))は偽になります。 – NielsK

答えて

4

全体魔法はclojure.lang.LazySeq Javaのクラスにあります。 lazy-seqマクロへのISeqインターフェイスとs-expressionsパラメータを実装するものは、パラメータなしの関数に変換され、clojure.lang.LazySeqのコンストラクタ(IFnオブジェクトをパラメータとして使用するコンストラクタ)に渡されます。 rがもう一度呼び出されました(これは完全リストではなくISeqを返しています)。これにより、LazySeqはアイテムを遅延評価することができます。

したがって、基本的流れはこのような何かを行く:

  • LazySeqは私(Fnは、渡された呼び出します。eコードの残りの部分)
  • このFn呼び出しは、リストがISeqを実装するため、ISeqを返します。これは具体的な値として最初の値を持つISeq(リスト)を返し、2番目はrへの再帰呼び出しのためにLazySeqオブジェクトです。この返されたISeqは、クラス内のローカル変数に格納されます。
  • 次の項目を呼び出す際のLazySeqのISeq実装は、上記の手順でローカルクラス変数に格納されているISeq(リスト)の次を呼び出し、LazySeq型であるかどうかを確認します(rコール)、それがLazySeqの場合はそれを評価し、アイテムを返す。アイテムを直接返す(コンサルトに渡した最初の具象値)

私はそれがちょっと心配なものだと知っている。私は今でもJavaコードを調べて、rへの再帰呼び出しがレイジーシーケンスを返すため、魔法が可能であることを認識した後で把握することができました。だからあなたはそれを持っている、カスタムの区切られた続きの種類:)

+0

'r'の戻り値は' head'の実現時にのみ 'r'を再度呼び出す' LazySeq'であるため、このイディオムに関連する再帰がないことを明確にすると便利です。実現は常に 'r'が既に返った後に起こります。 –

関連する問題