List CAR表示cons的*右*子树?

List CAR表示cons的*右*子树?,list,tree,lisp,common-lisp,List,Tree,Lisp,Common Lisp,我正在读Paul Graham的“ANSI Common Lisp”,3.8美元(第40页),内容如下: Conses也可以看作二叉树,car表示右子树,cdr表示左子树 对我来说,听起来好像“右”和“左”在相反的位置被错误地使用了……然后我看了一下在线勘误表,但它不是这里列出的项目之一 有人能帮忙澄清一下吗 顺便说一句,在下一页(第41页),Paul说“没有内部节点的二叉树没有多大用处。”我也不完全理解。“没有内部节点”是什么意思?一个深度嵌套的列表可以有任意多的内部/内部节点(即conse

我正在读Paul Graham的“ANSI Common Lisp”,3.8美元(第40页),内容如下:

Conses也可以看作二叉树,car表示右子树,cdr表示左子树

对我来说,听起来好像“右”和“左”在相反的位置被错误地使用了……然后我看了一下在线勘误表,但它不是这里列出的项目之一

有人能帮忙澄清一下吗

顺便说一句,在下一页(第41页),Paul说“没有内部节点的二叉树没有多大用处。”我也不完全理解。“没有内部节点”是什么意思?一个深度嵌套的列表可以有任意多的内部/内部节点(即conse),为什么他说“没有”?或者他可能只是指这些内部节点,如果存在,不包含原子值

谢谢,
/bruin

在Lisp语法中,
car
实际上是一对的左半部分
cdr
右半部分

至于内部节点,我想Graham是指未标记的内部节点。你会经常遇到像这样的树

(+ (* x y) z)
这应该被认为是由三元组构成的树,即使是以对的形式实现的,而不是形式的树

((a . b) . (c . d))
如果用某种形式的节点表示(二叉)树,我们有几种可能的方法用公共Lisp表示它们

我们可以定义一个节点结构:

(defstruct node left right)
   .
  / \
 a   .
    / \
   b   .
      / \
     c   .
        / \
       d   nil
然后我们有一个函数
MAKE-NODE

我们可以定义一个节点类:

(defclass node ()
  (left right))
节点将使用
(生成实例节点)
生成

我们可以使用列表创建节点:

(defun make-node (left right)
  (list left right))
上面使用了两个cons单元格

我们可以使用一个cons单元使二叉树的节点稍微小一些:

(defun make-node (left right)
  (cons left right))
我们可以将节点作为向量:

(defun make-node (left right)
  (vector left right))
有很多可能性。如果节点还应该有一些其他信息,那么单cons单元方法是不够的。类似于结构或类的东西很好,因为数据项可以有更多的信息,并且对象知道它的类型:我们可以很容易地询问
(node-p Something)
,并得到有用的答案

为要编写的稍高级的软件选择正确数据结构的样式规则:

  • 默认情况下使用CLOS
  • 如果CLOS太慢(例如插槽访问太慢),则尝试优化CLO
  • 如果CLOS仍然太慢,则使用结构。插槽访问应该更快
  • 尽量避免将数据结构表示为非类型化列表或cons树
Conses也可以看作是二叉树,与car 表示右子树,cdr表示左子树

你说得对,这似乎是非传统的。它可能应该说,
car
代表左子树,
cdr
代表右子树。也就是说,从cons单元构建的树(以及列表)是内涵数据结构。我们决定如何使用cons单元格表示结构,然后根据这种表示方式而不是cons单元格进行思考。所以,当我们编写列表处理代码时,我们更喜欢

(defun mapcar1 (fn list)
  (cons (funcall fn (first list))
        (mapcar fn (rest list))))

因为我们认为一个列表有第一个元素,列表中还有其他元素,尽管这两个元素都可以工作。由于实现的原因,
car
cdr
也能工作,这只是巧合。(当然,当我们注意到我们有一个名为
mapcar
nthcdr
的函数,而不是
map
(有一个
map
函数,但它有点不同)和
nthrest
nthtail
时,这是一个漏洞百出的抽象。)

类似地,我们必须选择用cons单元格表示二叉树。是否这样做实际上没有什么区别:

(defun make-tree (left right)
  (cons left right))

(defun left (tree)
  (car tree))

(defun right (tree)
  (cdr tree))

因为在处理树时,应该使用
生成树
,而不是
cons
汽车
cdr

没有内部节点的二叉树在很多方面都没有用处


他指的是二叉树,其内部节点没有自己的值。例如,在二叉搜索树中,节点不仅有左、右子树,还有关联的值(或元素)。二叉搜索树节点实际上是一个三元组(元素,左、右),而不是一对(左、右)。有些问题可以用节点没有关联值的二叉树来解决,但对于许多问题,除了一个值之外,您还需要包含两个指针(指向左子树和右子树)的节点。

在我看来,如果文本简单地说:

conse也可以被视为二叉树,car表示一个子树,cdr表示另一子树


左选哪个,右选哪个是任意的(当然,一致性等)也许文本已经引入了一个具体的表示法?或者可能是后来引入的,并且需要指出哪个是哪个?如果不是——如果这句话就是全部,那么我认为没有理由选择哪一边。

他声称汽车是右边的,cdr是左边的,这只是在装腔作势而已

回想一下,car/cdr最初出现在IBM 704上。在这台机器上,单词的car(即地址部分)位于右侧。第8页的图4说明了这一点。因此,我想他的陈述具有一定的历史效力


但我很好奇,他是否真的用他的左/右惯例绘制了类似于“(a b c d)的列表的插图。

这是一个错误。这是一个错误的原因是conses对应于一个打印的符号,并且打印的符号以一种特殊的方式从左到右

例如:

(a b c d) <==> (a . (b . (c . (d . nil))))
树不是一个简单的图;子图是有序的

如果
(a b c d) <==> (a . (b . (c . (d . nil))))
   .
  / \
 a   .
    / \
   b   .
      / \
     c   .
        / \
       d   nil