2

レイジーシーケンスを構築するときにflattenを使用しているときに観察される動作がわかりません。

clojure.coreのソースを見ると、flatten関数がfilterを呼び出しているため、遅延シーケンスを返す必要があることがわかります。それでも、次のスニペットはスタックオーバーフローエラーを表示します。スニペットでは、flattenの呼び出しがconcatの呼び出しに置き換えられた場合、問題なく機能します

(defn l-f [c]
  (if (nil? c) []
    (lazy-seq  (flatten (cons  [[ :h :j] :a :B] (l-f (rest c))))))) 


    (take 10 (l-f (repeat 2))) is how I invoke it.

これはかなり不自然な例です。また、flattenとconcatを使用すると、ネストレベルが異なるシーケンスが得られることも認識しています。

clojure.coreのコードについての私の(限られた)理解がそうではないと示唆しているにもかかわらず、なぜflattenが怠惰を壊しているように見えるのかを理解しようとしています。

4

1 に答える 1

5

怠惰はこれまでのところしか理解できません-怠惰とは、シーケンスが作成された時点で完全に実現されていないことを意味しますが、ある怠惰なシーケンスを別のシーケンスから構築するには、いくつかの値を先読みする必要がある場合があります。この場合、の実装はflatten、それを呼び出している再帰的な方法ではうまく機能しません。

最初に、flatten関数はtree-seqコレクションのコンテンツの深さ優先走査を実行するために呼び出します。次に、提供されたシーケンスを使用してtree-seq呼び出します。これはmapcatに委任さapplyれ、シーケンスの最初のいくつかの項目を実現して、呼び出す関数のアリティを決定します。シーケンスの最初のいくつかの項目を実現すると、への再帰呼び出しが発生し、残りの引数が呼び出され、無限ループでスタックしますl-fflatten

この特定の状況ではflatten、最初の呼び出しの後の呼び出しは効果がないため、再帰的に呼び出す必要はありません。したがって、レイジーシーケンスの生成をフラット化から分離することで、関数を修正できます。

(defn l-f [c]                                                      
  (letfn [(l-f-seq [x] (if-let [s (seq x)]                         
                         (lazy-seq (cons [[:h :j] :a :B] (l-f-seq (rest s))))
                         []))]                                               
    (flatten (l-f-seq c))))
于 2012-09-27T18:21:42.727 に答える