cons
はseqを返し、conj
はコレクションを返します。また、conj
はコレクションの最適な末尾にアイテムを「追加」し、cons
は常にアイテムを先頭に「追加」することも知っています。この例は、これらの両方のポイントを示しています。
user=> (conj [1 2 3] 4) //returns a collection
[1 2 3 4]
user=> (cons 4 [1 2 3]) //returns a seq
(4 1 2 3)
ベクトル、マップ、およびセットの場合、これらの違いは理にかなっています。ただし、リストの場合は同じように見えます。
user=> (conj (list 3 2 1) 4) //returns a list
(4 3 2 1)
user=> (cons 4 (list 3 2 1)) //returns a seq
(4 3 2 1)
conj
とcons
が異なる動作を示すリストを使用した例はありますか、それとも本当に互換性がありますか?別の言い方をすると、リストとシーケンスを同等に使用できない例はありますか?
1つの違いは、conj
はコレクションに挿入するために任意の数の引数を受け入れますが、cons
は1つだけをとることです。
(conj '(1 2 3) 4 5 6)
; => (6 5 4 1 2 3)
(cons 4 5 6 '(1 2 3))
; => IllegalArgumentException due to wrong arity
もう1つの違いは、戻り値のクラスです。
(class (conj '(1 2 3) 4))
; => clojure.lang.PersistentList
(class (cons 4 '(1 2 3))
; => clojure.lang.Cons
これらは実際には互換性がないことに注意してください。特に、clojure.lang.Cons
はclojure.lang.Counted
を実装しないため、その上のcount
は一定時間の操作ではなくなります(この場合、おそらく1 + 3に減少します-1最初の要素に対する線形トラバーサルに由来し、3は(next (cons 4 '(1 2 3))
がPersistentList
であり、したがってCounted
であることに起因します。
名前の背後にある意図は、cons
がcons(truct a seq)を意味すると信じている1一方、conj
はconj(アイテムをコレクションに追加する)を意味します。 seq
で構成されているcons
は、最初の引数として渡された要素から始まり、そのnext
/rest
部分として、 seq
を2番目の引数に。上記のように、全体はclojure.lang.Cons
クラスです。対照的に、conj
は常に、渡されたコレクションとほぼ同じタイプのコレクションを返します。 (おおよそ、PersistentArrayMap
は9エントリを超えるとすぐにPersistentHashMap
に変換されるためです。)
1 従来、LISPの世界ではcons
cons(ペアを構成)であるため、Clojureはcons
関数が従来のcdr
。 「何らかの種類のレコードを構築していくつかの値を保持する」という意味のcons
の一般的な使用法は、現在、プログラミング言語とその実装の研究で広く普及しています。それが「コンシングの回避」が言及されている場合の意味です。
私の理解では、あなたの言うことは真実だということです。リスト上のconjはリスト上のconsと同等です。
Conjは「どこかに挿入」操作であり、consは「先頭に挿入」操作であると考えることができます。リストでは、先頭に挿入するのが最も論理的であるため、この場合conjとconsは同等です。
別の違いは、conj
がシーケンスを最初の引数としてとるので、alter
をあるシーケンスに更新するとき、ref
でうまく動作することです。
(dosync (alter a-sequence-ref conj an-item))
これは基本的に(conj a-sequence-ref an-item)
スレッドセーフな方法で。これはcons
では機能しません。詳細については、Stu Hallowayによる Programming Clojure の並行性に関する章を参照してください。
別の違いはリストの動作ですか?
(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false