我听说Clojure不像大多数Lisp语言一样具有cons单元格。
这是否意味着Clojure列表不以空列表结尾?
有人能解释一下这到底是什么意思吗?
我听说Clojure不像大多数Lisp语言一样具有cons单元格。
这是否意味着Clojure列表不以空列表结尾?
有人能解释一下这到底是什么意思吗?
( a . b )
。
使用列表符号简写某些符号表达式:(a b c)
。
使用原子符号nil
终止列表。
基本函数包括:car
、cdr
、cons
、eq
和atom
。
还有其他几个函数:ff
、subst
、equal
、null
、cadr
、caddr
、null
、append
、among
、pair
、assoc
、sublis
、apply
、eval
等。rplaca
(表示替换car)和rplacd
(表示替换cdr)。请参阅1962年John McCarthy等人的LISP 1.5程序员手册。这些函数允许我们编写破坏性函数,并允许我们创建基于循环cons的数据结构,例如循环列表。
通用Lisp
通常Lisp方言实现大部分功能。通用Lisp也不例外,该功能在其标准中描述:Conses。下面是使用上述函数的示例:
; pair two lists into a list of cons cells.
; the function pair is called pairlis in Common Lisp.
CL-USER 17 > (pairlis '(john mary eva) '(34 29 40))
((EVA . 40) (MARY . 29) (JOHN . 34))
; find a cons cell in a list of cons cells,
; based on the content of the car of those cons cells
CL-USER 18 > (assoc 'eva (pairlis '(john mary eva)
'(34 29 40)))
(EVA . 40)
; create a tree out of cons cells and atoms
CL-USER 19 > (cons (cons 10 20) (cons 30 40))
((10 . 20) 30 . 40)
; a cons cell is not an atom
CL-USER 20 > (atom (cons 1 2))
NIL
; a cons cell is not nil
CL-USER 21 > (null (cons 1 2))
NIL
; substitute an item with a new one in a tree
CL-USER 22 > (subst 30 ; new
'bar ; old
'((10 . 20) . (bar . 40))) ; tree
((10 . 20) 30 . 40) ; also written as ((10 . 20) . (30 . 40))
; substitute several items in a tree, using an assoc list
; to describe the substitutions
CL-USER 23 > (sublis '((a . 10) (d . 40)) ; substitutions
'((a . b) . (c . d))) ; tree
((10 . B) C . 40)
列表是符号表达式的一种特殊情况。它们通常不带点写成:
CL-USER 24 > '(a . (b . nil))
(A B)
Common Lisp也支持Lisp 1.5的可变操作rplaca
和rplacd
:
CL-USER 25 > (let ((c (cons 0 1))) ; create a cons
(print c) ; print it
(print (rplaca c 'foo)) ; replace the car
(print (rplacd c 'bar)) ; replace the cdr
(print (eq c (rplaca c 'baz))) ; identical ?
(values))
(0 . 1) ; the cons cell
(FOO . 1) ; car replaced
(FOO . BAR) ; cdr replaced
T ; still the same object
Emacs Lisp
Emacs Lisp也实现了上述功能:
ELISP> (sublis '((a . 10) (d . 40))
'((a . b) . (c . d)))
((10 . b) c . 40)
Clojure
Clojure不支持John McCarthy所描述的这些符号表达式。它没有cons单元,没有点表示法,并且不提供上述接口。例如,在Clojure中,atom表示完全不同的东西。cons
不创建cons单元。列表不是由cons单元组成的。
在Clojure中,点只是另一个符号:
user=> (count '(1 . 2))
3
有一个原始函数用于构建列表:
user=> (list 1 2 3)
(1 2 3)
user=> (list? (list 1 2 3))
true
cons
的函数:user=> (cons 0 (list 1 2 3))
(0 1 2 3)
不知何故这不是一个列表:
user=> (list? (cons 0 (list 1 2 3)))
false
基本上,Clojure使用不同的数据结构(-> 序列, 逻辑列表),具有自己的命名和语义。即使名称类似于Lisp名称,也不要期望它们执行相同的操作。
Scheme
编程语言Scheme也提供类似上述的cons单元。它缺少一些函数,但是可以很容易地实现它们。例如,sublis
在Scheme中可以像这样实现(见initdr.scm):
(define (sublis alist tree)
(if (pair? tree)
(cons (sublis alist (car tree))
(sublis alist (cdr tree)))
(if (assv tree alist)
(cdr (assv tree alist))
tree)))
cons、first 和 rest 操作序列抽象,而不是具体的 cons 单元。
Clojure 列表不以空列表结尾,也不是传统的 cons 单元。它们是实现序列的数据结构。这篇关于编写抽象代码的文章解释了 Clojure 对“seqable”结构(包括列表)的处理方式:
一般来说,编写抽象代码可以让您使用各种不同的数据结构库函数,而不用管这些数据结构的实现方式如何。
因此,Clojure 列表类似于 cons 单元,因为它们实现了 cons
、first
和 rest
,但这仅表示它们共享相同的接口。它们的底层实现不同,但它们都是“seqable”的。
clojure.lang.Cons
。cons
调用的结果。rest
/cdr
是一个序列,而不是一个Object
。cons
,你将得到一个Cons
。Cons
的函数。它们都处理一般序列。另外一个用途:对一个不确定的序列(既不是向量、列表、集合、字典),进行conj
操作会产生一个Cons
。
nil
。 nil
表示列表的末尾。Clojure通过其列表提供了大致相同的功能,但底层表示不同。它确实有一个名为Cons
的数据类型,但并非所有列表或给定列表的所有部分都是由Cons
构建的。(如果您还没有阅读jmargolisvt的答案,请现在阅读。)[编辑:其他答案表明,我关于Clojure中列表和Cons之间关系的说法是不正确的。在“列表”的非正式意义上,人们可能会觉得它是正确的-或者不是。]user=> (def foo (list 1))
#'user/foo
user=> foo
(1)
user=> (class foo)
clojure.lang.PersistentList
user=> (def bar (cons 2 foo))
#'user/bar
user=> bar
(2 1)
user=> (class bar)
clojure.lang.Cons
user=> (next bar)
(1)
user=> (rest bar)
(1)
user=> (class (next bar))
clojure.lang.PersistentList
user=> (class (rest bar))
clojure.lang.PersistentList
user=> (next foo)
nil
user=> (rest foo)
()
user=> (= nil ())
false
user=> (rest ())
()
user=> (rest nil)
()
user=> (next ())
nil
user=> (next nil)
nil
nil
之外的另一个对象上。结果是一个“点对列表”(1 . 2)
,它是一个单一的cons单元,其中cdr指针指向除了另一个cons单元或nil
之外的其他内容,就像在正常列表中一样。让我们在Clojure中尝试一下:user=> (cons 1 2)
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:528)
顺便说一下,与Common Lisp相比(其中nil
= ()
= false),还有一个显著的区别:
user=> (= nil false)
false
user=> (= () false)
false
然而,虽然nil
不是false
,但你可以像使用false
一样使用它:
user=> (if nil "nil works like true" "nil works like false")
"nil works like false"
然而,您无法使用空列表执行此操作:
user=> (if () "() works like true" "() works like false")
"() works like true"
sequence
类型比list
更广泛吗?所以我所称呼的list
实际上是一个sequence
?(那会让我感到困惑!)例如,向量也是sequence
吗?因此,也许序列概念在两种语言中的作用是类似的? - Mars(cons 1 2)
会因为某些Java级别的错误而崩溃,并指向Java代码中的行号。太棒了! - Kaz
cons
在列表前构建(扩展)列表,但是(从我在Rich Hickey的YouTube视频中看到的)Clojure使用conj
在数组后面构建其支持列表。 - Will Ness