我听说,clojure在大多数lisp语言中都没有cons细胞。
这是否意味着Clojure列表不以空列表结尾?
任何人都可以解释这到底意味着什么吗?
Lisp提供了原始的cons数据结构及其表示法。
该章介绍:
( 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
,...早在Lisp中,已添加了用于更改cons细胞的功能:rplaca
(表示replace car)和rplacd
(表示replace cdr)。参见LISP 1.5 Programmer's Manual by John McCarthy et al. from 1962。这些函数使我们能够编写破坏性函数,也使我们能够创建基于循环cons的数据结构,例如循环列表。
Common Lisp
通常,Lisp方言实现了大部分功能。 Common Lisp也不例外,其功能在Common 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
如John McCarthy所述,Clojure不支持这些符号表达式。它没有缺点单元格,没有点符号,并且不提供上述接口。例如atom在Clojure中的含义完全不同。 cons
不会创建约束单元。列表不是由cons单元组成的。
在Clojure中,点只是另一个符号:
user=> (count '(1 . 2))
3
有一个原始函数可以构造lists:
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
Basic Clojure确实使用不同的数据结构(-> sequences,逻辑列表),并具有自己的命名和语义。即使名称与Lisp名称相似,也不要指望它们具有相同的功能。
方案
编程语言Scheme也提供类似于上面的con单元。它缺少某些功能,但可以轻松实现。例如,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)))
clojure.lang.Cons
。cons
调用的结果rest
/cdr
是一个序列,而不是Object
。cons
内容添加到列表,向量或惰性序列中,您得到Cons
。Cons
es。它们一般都按顺序处理。 [另一种用法:conj
插入不确定的序列(既不是向量列表,也不是set或map ...)会产生Cons
。
cons,first和rest操纵序列抽象,而不是具体的cons单元格
Clojure列表不以空列表结尾,并且它们不是传统的con单元格。它们是实现排序的数据结构。 This page on programming to abstractions解释了Clojure的“适当”结构的方法,包括列表:
通常,抽象编程使您能够使用不同数据结构上的函数库,而不管这些数据结构如何实现,从而为您提供了强大的功能。
因此Clojure列表就像cons单元格一样,它们实现了cons
,first
和rest
,但这仅意味着它们共享一个公共接口。它们的基础实现有所不同,但是它们都是“适当的”。
在Common Lisp中,列表是con单元格的序列。每个缺点单元有两个插槽或指针,分别称为“ car”和“ cdr”。汽车指向(或持有)任何东西。 cdr通常指向另一个cons单元格或nil
。 nil
计为列表的末尾。 Clojure为其列表提供了大致相同的功能,但基础表示形式有所不同。它确实具有称为Cons
的数据类型,但并非所有列表或给定列表的所有部分都是根据Cons
构建的。 (现在,如果您尚未阅读jmargolisvt的答案,则应该阅读。)[编辑:其他答案表明,我在这里所说的关于Clojure中的列表和Conses之间的关系是不正确的。有人可能会觉得它在非正式的“列表”意义上是正确的,或者不是。]
还请注意,部分由于序列抽象的思想,列表本身在Clojure中的通用性远低于Common Lisp或Scheme。但是,其他类型的序列也很常见。
值得一提的是,在Clojure中,您不能假设在打印输出时看起来像列表的东西实际上就是列表。例如,它可能是一个惰性序列,被not视为列表。
以下是一些可能使用列表的Clojure示例:
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
(即使foo
返回不同的数据类型,bar
和class
都被视为列表。]
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
在Common Lisp中,您可以将一个对象限制到列表或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
=()
=否::
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"
“它自己的美。)