(conj collection item)
に追加item
しcollection
ます。そのためには、実現する必要があります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
か?(どのようCons
にLazySeq
オブジェクトが連携してレイジーシーケンスを形成するかは、以下でより明確になります。)の98行clojure/src/jvm/clojure/lang/LazySeq.java
目を見てください。と呼ばれるメソッドを呼び出すことに注意してくださいseq
。これが(詳細については55行LazySeq
目にジャンプ)の値を実現するものです。
conj
つまり、通過したコレクションの種類を正確に知る必要があると言えますが、そうでcons
はありません。cons
「collection」引数が。である必要がありISeq
ます。
Cons
Clojureのオブジェクトは、他のLispの「conscells」とは異なることに注意してください。ほとんどのLispでは、「cons」は、他の任意のオブジェクトへの2つのポインターを保持する単なるオブジェクトです。したがって、consセルを使用してツリーを構築することができます。ClojureCons
は、任意Object
の頭を頭、ISeq
尾をとっています。それCons
自体がを実装ISeq
しているため、オブジェクトからシーケンスを構築できますがCons
、ベクトルやリストなどを指すこともできます(Clojureの「リスト」は特殊なタイプ(PersistentList
)であり、オブジェクトから構築されていないCons
ことに注意してください。 )clojure.lang.LazySeq
も実装ISeq
しているため、。のテール(Lispsでは「cdr」)として使用できますCons
。LazySeq
ISeq
ある種のコードに変換しますが、実際には必要になるまでそのコードを評価せず、コードを評価した後、返されたコードをキャッシュして委任ISeq
します。
...これはすべて意味をなし始めていますか?怠惰なシーケンスがどのように機能するかを理解していますか?基本的には、から始めますLazySeq
。が実現されると、別のを指す、LazySeq
に評価されます。それが実現したとき...あなたはアイデアを得る。したがって、オブジェクトのチェーンを取得し、それぞれがを保持(および委任)します。Cons
LazySeq
LazySeq
Cons
Clojureの「conses」と「lists」の違いについては、「lists」(PersistentList
オブジェクト)にはキャッシュされた「length」フィールドが含まれているためcount
、O(1)時間で応答できます。ほとんどのLispでは「リスト」は変更可能であるため、これは他のLispでは機能しません。しかし、Clojureではそれらは不変であるため、長さをキャッシュすることは機能します。
Cons
Clojureのオブジェクトにはキャッシュされた長さがありません-もしそうなら、それらをどのように使用して怠惰な(そして無限の)シーケンスを実装できますか?のを取得しようとするとcount
、テールをCons
呼び出すだけで、結果が1ずつ増加します。count