2010-06-09 51 views
89

我知道cons返回一個seq,而conj返回一個集合。我也知道conj會將該項目「添加」到該集合的最佳結尾,並且cons總是將該項目「添加」到前面。這個例子說明了這兩點:Clojure:cons(seq)vs. conj(list)

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) 

是否有使用列表,其中conjcons表現出不同的行爲,任何的例子,或者是他們真正的互換?換句話說,是否有一個例子,列表和seq不能等價使用?

回答

140

一個區別是,conj接受任何數量的參數插入到一個集合,而cons只需一個:

(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 

另一個不同之處是班上的返回值:

(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意味着利弊(truct一SEQ),而conj裝置以連詞(OIN項目到集合)。由cons構造的seq以作爲其第一個參數傳遞的元素開始,並且具有作爲其/rest部分的由於將seq應用於第二個參數而產生的部分;如上所示,整個事情是類clojure.lang.Cons。相反,conj始終返回與傳遞給它的集合大致相同類型的集合。 (粗略地說,因爲一個PersistentArrayMap將盡快變成PersistentHashMap因爲它的增長超出9項。)


傳統上,在Lisp中世界,cons利弊(tructs一對),所以Clojure的離去從Lisp傳統的cons函數構造一個沒有傳統cdr的seq。 cons的一般用法意指「構造某種類型的記錄或將其保存在一起的多個值」,目前在編程語言及其實現的研究中無處不在;這就是當提到「避免顧慮」時的意思。

+1

這是多麼美妙的文章!我不知道有一個Cons類型。做得好! – 2010-06-09 21:18:45

+0

謝謝。高興聽到。 :-) – 2010-06-09 21:25:32

+2

順便說一句,作爲一個特殊情況,'(cons foo nil)'返回一個單例PersistentList'(同樣對'conj')。 – 2010-06-09 21:30:02

9

我的理解是你說的是真的:列表上的conj相當於列表上的缺點。

你可以認爲conj是一個「插入某處」操作,並且認爲是「插入頭部」操作。在列表中,插入頭部是最合理的,因此在這種情況下,conj和cons是等價的。

8

另一個不同之處是,由於conj接受一個序列作爲第一個參數,它更新ref一些序列時alter起到很好:

(dosync (alter a-sequence-ref conj an-item)) 

這基本上不會在一個線程安全的方式(conj a-sequence-ref an-item)。這不適用於cons。請參閱Stu Halloway在Programming Clojure中有關併發的一章以獲取更多信息。

1

另一個區別是列表的行爲?

(list? (conj() 1)) ;=> true 
(list? (cons 1())) ; => false 
+4

cons總會返回一個連接,它會返回與所提供的類型相同的類型 – 2014-01-27 07:55:41