25

consがlazy-seqでこのコンテキストで機能するのに、conjが機能しないのはなぜですか?

これは機能します:

(defn compound-interest [p i]
   (cons p (lazy-seq (compound-interest (* p (+ 1 i)) i))))

これはしません(スタックオーバーフロー例外が発生します):

(defn compound-interest2 [p i]
   (conj (lazy-seq (compound-interest2 (* p (+ 1 i)) i)) p))
4

1 に答える 1

46

(conj collection item)に追加itemcollectionます。そのためには、実現する必要がありますcollection。(理由は以下で説明します。)したがって、再帰呼び出しは延期されるのではなく、すぐに発生します。

(cons item collection)で始まりitem、その後に。のすべてが続くシーケンスを作成しcollectionます。重要なのは、それを実現する必要がないcollectionということです。したがって、再帰呼び出しは、lazy-seq誰かが結果のシーケンスの末尾を取得しようとするまで(を使用しているため)延期されます。

これが内部でどのように機能するかを説明します。

cons実際にはclojure.lang.Consオブジェクトを返します。これは、レイジーシーケンスの構成要素です。conj渡したのと同じタイプのコレクションを返します(リスト、ベクター、その他)。conjこれは、コレクション自体に対するポリモーフィックJavaメソッド呼び出しを使用して行います。(の524行目clojure/src/jvm/clojure/lang/RT.javaを参照してください。)

そのJavaメソッド呼び出しがclojure.lang.LazySeqによって返されるオブジェクトで発生するとどうなりますlazy-seqか?(どのようConsLazySeqオブジェクトが連携してレイジーシーケンスを形成するかは、以下でより明確になります。)の98行clojure/src/jvm/clojure/lang/LazySeq.java目を見てください。と呼ばれるメソッドを呼び出すことに注意してくださいseq。これが(詳細については55行LazySeq目にジャンプ)の値を実現するものです。

conjつまり、通過したコレクションの種類を正確に知る必要があると言えますが、そうでconsはありません。cons「collection」引数が。である必要がありISeqます。

ConsClojureのオブジェクトは、他のLispの「conscells」とは異なることに注意してください。ほとんどのLispでは、「cons」は、他の任意のオブジェクトへの2つのポインターを保持する単なるオブジェクトです。したがって、consセルを使用してツリーを構築することができます。ClojureConsは、任意Objectの頭を頭、ISeq尾をとっています。それCons自体がを実装ISeqしているため、オブジェクトからシーケンスを構築できますがCons、ベクトルやリストなどを指すこともできます(Clojureの「リスト」は特殊なタイプ(PersistentList)であり、オブジェクトから構築されていないConsことに注意してください。 )clojure.lang.LazySeq 実装ISeqしているため、。のテール(Lispsでは「cdr」)として使用できますConsLazySeqISeqある種のコードに変換しますが、実際には必要になるまでそのコードを評価せず、コードを評価した後、返されたコードをキャッシュして委任ISeqします。

...これはすべて意味をなし始めていますか?怠惰なシーケンスがどのように機能するかを理解していますか?基本的には、から始めますLazySeq。が実現されると、別のを指す、LazySeqに評価されます。それが実現したとき...あなたはアイデアを得る。したがって、オブジェクトのチェーンを取得し、それぞれがを保持(および委任)します。ConsLazySeqLazySeqCons

Clojureの「conses」と「lists」の違いについては、「lists」(PersistentListオブジェクト)にはキャッシュされた「length」フィールドが含まれているためcount、O(1)時間で応答できます。ほとんどのLispでは「リスト」は変更可能であるため、これは他のLispでは機能しません。しかし、Clojureではそれらは不変であるため、長さをキャッシュすることは機能します。

ConsClojureのオブジェクトにはキャッシュされた長さがありません-もしそうなら、それらをどのように使用して怠惰な(そして無限の)シーケンスを実装できますか?のを取得しようとするとcount、テールをCons呼び出すだけで、結果が1ずつ増加します。count

于 2012-09-12T14:21:10.473 に答える