Clojure: cons (seq) vs. conj (list)

后端 未结 5 657
忘掉有多难
忘掉有多难 2020-12-04 05:17

I know that cons returns a seq and conj returns a collection. I also know that conj \"adds\" the item to the optimal end of the colle

相关标签:
5条回答
  • 2020-12-04 06:00

    Another difference is that because conj takes a sequence as the first argument, it plays nicely with alter when updating a ref to some sequence:

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

    This basically does (conj a-sequence-ref an-item) in a thread-safe manner. This wouldn't work with cons. See the chapter on Concurrency in Programming Clojure by Stu Halloway for more info.

    0 讨论(0)
  • 2020-12-04 06:09

    My understanding is that what you say is true: conj on a list is equivalent to cons on a list.

    You can think of conj as being an "insert somewhere" operation, and cons as being an "insert at the head" operation. On a list, it is most logical to insert at the head, so conj and cons are equivalent in this case.

    0 讨论(0)
  • 2020-12-04 06:09

    There are dedicated functions in the Tupelo Library to add append or prepend values to any sequential collection:

    (append [1 2] 3  )   ;=> [1 2 3  ]
    (append [1 2] 3 4)   ;=> [1 2 3 4]
    
    (prepend   3 [2 1])  ;=> [  3 2 1]
    (prepend 4 3 [2 1])  ;=> [4 3 2 1]
    
    0 讨论(0)
  • 2020-12-04 06:12

    Another difference is the behavior of list?

    (list? (conj () 1)) ;=> true
    (list? (cons 1 ())) ; => false
    
    0 讨论(0)
  • 2020-12-04 06:19

    One difference is that conj accepts any number of arguments to insert into a collection, while cons takes just one:

    (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
    

    Another difference is in the class of the return value:

    (class (conj '(1 2 3) 4))
    ; => clojure.lang.PersistentList
    
    (class (cons 4 '(1 2 3))
    ; => clojure.lang.Cons
    

    Note that these are not really interchangeable; in particular, clojure.lang.Cons does not implement clojure.lang.Counted, so a count on it is no longer a constant time operation (in this case it would probably reduce to 1 + 3 -- the 1 comes from linear traversal over the first element, the 3 comes from (next (cons 4 '(1 2 3)) being a PersistentList and thus Counted).

    The intention behind the names is, I believe, that cons means to cons(truct a seq)1, whereas conj means to conj(oin an item onto a collection). The seq being constructed by cons starts with the element passed as its first argument and has as its next / rest part the thing resulting from the application of seq to the second argument; as displayed above, the whole thing is of class clojure.lang.Cons. In contrast, conj always returns a collection of roughly the same type as the collection passed to it. (Roughly, because a PersistentArrayMap will be turned into a PersistentHashMap as soon as it grows beyond 9 entries.)


    1 Traditionally, in the Lisp world, cons cons(tructs a pair), so Clojure departs from the Lisp tradition in having its cons function construct a seq which doesn't have a traditional cdr. The generalised usage of cons to mean "construct a record of some type or other to hold a number of values together" is currently ubiquitous in the study of programming languages and their implementation; that's what's meant when "avoiding consing" is mentioned.

    0 讨论(0)
提交回复
热议问题