[英]Clojure: cons (seq) vs. conj (list)
我知道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
表現出不同的行為,或者它們真的可以互換? 換種說法,是否有列表和 seq 不能等效使用的示例?
一個區別是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
意味着 cons(truct a seq) 1 ,而conj
意味着 conj(oin an item into a collection)。 cons
構造的seq
以作為其第一個參數傳遞的元素開始,並將seq
應用於第二個參數所產生的結果作為其next
/ rest
部分; 如上所示,整個內容屬於clojure.lang.Cons
類。 相反, conj
總是返回與傳遞給它的集合大致相同類型的集合。 (粗略地說,因為PersistentArrayMap
一旦超過 9 個條目就會變成PersistentHashMap
。)
1傳統上,在 Lisp 世界中, cons
cons(tructs a pair),因此 Clojure 與 Lisp 傳統不同,它的cons
函數構造了一個沒有傳統cdr
的 seq。 cons
的廣義用法意味着“構建某種類型或其他類型的記錄以將多個值保存在一起”目前在編程語言及其實現的研究中無處不在。 這就是提到“避免 consing”時的意思。
我的理解是你說的是真的:列表上的 conj 相當於列表上的 cons。
您可以將 conj 視為“在某處插入”操作,而將 cons 視為“在頭部插入”操作。 在列表中,最合乎邏輯的是插入到頭部,所以在這種情況下 conj 和 cons 是等價的。
另一個區別是,因為conj
將序列作為第一個參數,所以在更新某個序列的ref
時,它可以很好地與alter
配合使用:
(dosync (alter a-sequence-ref conj an-item))
這基本上以線程安全的方式執行(conj a-sequence-ref an-item)
。 這不適用於cons
。 有關更多信息,請參閱 Stu Halloway編寫的編程 Clojure 中的並發一章。
另一個區別是列表的行為?
(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.