(conj collection item)
は、item
からcollection
を加算する。そのためには、collection
を実現する必要があります。 (私は以下の理由を説明します)。したがって、再帰呼び出しは、遅延されるのではなく、ただちに行われます。
(cons item collection)
は、item
で始まり、次にすべてがcollection
で始まるシーケンスを作成します。意義深く、それはcollection
を実現する必要はありません。したがって、再帰呼び出しは、(lazy-seq
を使用しているため)誰かが結果のシーケンスの末尾を取得するまで延期されます。
私はこれが内部でどのように機能するかを説明します:
cons
は実際に怠惰なシーケンスがで作られているものであるclojure.lang.Cons
オブジェクトを返します。 conj
は、それが渡されたコレクションと同じタイプのコレクションを返します(リスト、ベクターなど)。 conj
は、コレクション自体に対して多形Javaメソッド呼び出しを使用してこれを行います。 (line 524 of clojure/src/jvm/clojure/lang/RT.java
を参照してください)
オブジェクトのJavaメソッド呼び出しがlazy-seq
によって返されるとどうなりますか? (Cons
とLazySeq
オブジェクトが一緒になってレイジーシーケンスを形成する方法は、以下でより明確になります)。line 98 of clojure/src/jvm/clojure/lang/LazySeq.java
を見てください。これは、seq
というメソッドを呼び出していることに注意してください。これはLazySeq
の値を実現するものです(詳細はline 55にジャンプしてください)。
conj
は、あなたが渡したコレクションの種類を正確に知る必要があると言えるでしょうが、cons
はそうではありません。 cons
"collection"引数がISeq
であることが必要です。
Clojureのオブジェクトは、他のLispsの「コンスセル」とは異なります。ほとんどのLispsでは、「cons」は他の任意のオブジェクトへの2つのポインタを保持するオブジェクトです。したがって、コンスセルを使用してツリーを構築することができます。 Clojure Cons
は任意のObject
を頭に、ISeq
をテールとして取ります。 Cons
自体はISeq
を実装しているので、Cons
オブジェクトからシーケンスを構築できますが、ベクトルやリストなどを指すこともできます(Clojureの「リスト」は特殊タイプ(PersistentList
)で、Cons
オブジェクトから構築されたではありません。)clojure.lang.LazySeq
はを実装していますので、Cons
のテール( "Lis"の "cdr")として使用できます。LazySeq
は、が何らかの種類の〜ISeq
を評価するコードへの参照を保持しますが、実際にコードが評価されるまでは実際には評価されず、コードを評価した後に返されたISeq
をキャッシュして代行します。
...これはすべて理にかなっていますか?あなたはレイジーシーケンスがどのように機能するか考えていますか?基本的には、LazySeq
で始まります。 LazySeq
が実現されると、Cons
と評価され、LazySeq
を指します。そのことが実現すると、あなたはそのアイデアを得ます。だからのチェーンがあり、それぞれがCons
を保持(および委任)しています。
Clojureの "conses"と "lists"の違いについて、 "リスト"(PersistentList
オブジェクト)にはキャッシュされた "長さ"フィールドが含まれているため、O(1)時間でcount
に応答できます。ほとんどのLispsでは "リスト"が変更可能であるため、これは他のLispsでは機能しません。しかし、Clojureではそれらは不変なので、長さのキャッシュは機能します。 Clojureので
Cons
オブジェクトがは、キャッシュされた長さを持っていない - 彼らがした場合、彼らは怠惰な(とでも無限の)配列を実装するために使用することができる方法? Cons
のcount
を取得しようとすると、ちょうどその末尾にcount
が呼び出され、結果が1だけ増えます。
ここに来る他の人のために:私の答えは非常に詳細です(おそらくあまりにも詳細です)。混乱する。ここでの主なポイントを繰り返してみましょう: 'conj'のセマンティクスは、コレクション型に基づいてその振る舞いを変える必要があります。そのためには、コレクションオブジェクトに対して(多態的な)メソッド呼び出しを使用する必要があります。 'LazySeq'は、その内部値を実現する必要がある内部値に委譲することによってそのメソッド呼び出しを処理します。対照的に、consのセマンティクスではコレクションのメソッドを呼び出す必要はありません。 'Cons'オブジェクトの1つのフィールドに格納するだけです。 –
答えの最後の3つの段落が特に役立ちます。一部の人は最初にそれを読んでみたいかもしれません。 – Mars