CAR表示锥形的*右*子树?

时间:2013-11-19 14:37:48

标签: list tree lisp common-lisp

我正在阅读Paul Graham撰写的“ANSI Common Lisp”,价格为3.8美元(第40页):

“Conses也可以被视为二叉树,汽车代表正确的子树,cdr代表左侧。”

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

http://www.paulgraham.com/ancomliser.html

任何人都可以帮忙解释一下吗?

顺便说一下,在下一页(第41页),保罗说“没有内部节点的二叉树没有用处。”我也不完全理解它。 “没有内部节点”是什么意思?一个深层嵌套的列表可以拥有你想要的内部/内部节点(即,conses),为什么他说“没有”?或者他只是意味着这些内部节点(如果存在)不包含原子值?

谢谢, /布鲁因

6 个答案:

答案 0 :(得分:3)

在Lisp语法中,car确实是右半部分cdr的左半部分。

对于内部节点,我认为Graham意味着未标记的内部节点。例如。你会经常遇到像

这样的树
(+ (* x y) z)

应该被认为是由三元组构建的树,即使是以对形式实现的,也不是形式的树

((a . b) . (c . d))

答案 1 :(得分:3)

  

Conses也可以被认为是汽车的二叉树   表示正确的子树和表示左侧的cdr。

你认为这似乎是非常规的。应该说car代表 left 子树,而cdr代表 right 子树。也就是说,从cons单元构建的树(以及列表)是内涵数据结构。我们决定如何使用cons单元表示结构,然后根据该表示进行思考,而不是考虑cons单元。因此,当我们编写列表处理代码时,我们更喜欢

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

(defun mapcar1 (fn list)
  (cons (funcall fn (car list))
        (mapcar fn (cdr list))))

因为我们认为列表具有第一个元素,并且列表中存在 rest ,即使两者都有效。由于实施,carcdr也会起作用,这只是巧合。 (当然,当我们注意到我们有名为mapcarnthcdr而不是map的函数时,这是一个漏洞抽象(好吧,一个map函数,但它有点不同)和nthrestnthtail

同样,我们必须选择使用cons单元表示二叉树。无论你是否真的

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

(defun left (tree)
  (car tree))

(defun right (tree)
  (cdr tree))

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

(defun left (tree)
  (cdr tree))

(defun right (tree)
  (car tree))

因为您应该使用make-treeleftright而不是conscar和{ {1}}当您使用树木时。

  

没有内部节点的二叉树没有多大用处。

他指的是二进制树,其内部节点不具有自己的值。例如,在二叉搜索树中,节点不仅具有左子树和右子树,它具有关联的值(或元素)。二叉搜索树节点实际上是三元组(元素,左,右),而不是一对(左,右)。一些问题可以通过二进制树来解决,二进制树的节点没有相关的值,但是对于许多问题,除了一个值之外,你还希望节点有两个指针(左右子树)。 / p>

答案 2 :(得分:2)

如果您使用某种形式的节点表示(二进制)树,我们有几种可能的方法在Common Lisp中表示它们。

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

(defstruct node left right)

然后我们有一个函数MAKE-NODE

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

(defclass node ()
  (left right))

节点将使用(make-instance 'node)

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

(defun make-node (left right)
  (list left right))

上面使用了两个利弊细胞。

我们可以只使用一个cons单元格为二元树创建一个小的节点:

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

我们可以将节点作为向量:

(defun make-node (left right)
  (vector left right))

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

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

  • 默认情况下使用CLOS
  • 如果CLOS太慢(例如插槽访问速度太慢),则尝试优化CLOS
  • 如果CLOS仍然太慢,那么使用结构。插槽访问应该更快
  • 尽量避免将数据结构表示为无类型列表或缺点树

答案 3 :(得分:2)

国际海事组织,如果案文简单地说的话会更好:

Conses也可以被视为二叉树,汽车代表一个子树,cdr代表其他子树。

左边选择哪个,右边选择哪个是任意的。 (当然,一致性等)或许文本已经引入了具体的表述?或许它之后会这样做,需要参考哪个是哪个?如果不是 - 如果那个陈述完全存在,那么我认为没有理由选择双方。

答案 4 :(得分:2)

当他断言汽车是正确的并且司机是左边时,他只是很可爱?

回想一下,汽车/ cdr最初是在IBM 704上产生的。在那台机器上,汽车(即地址部分)的单词是右侧。这个one of the 704 manuals第8页的图4说明了这一点。所以我猜他的演讲有一些历史的有效性。

但我很好奇他是否真的有使用他的左/右惯例绘制的'(a b c d)等列表的插图。

答案 5 :(得分:1)

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

示例:

(a b c d) <==> (a . (b . (c . (d . nil))))

这对应于树结构:

   .
  / \
 a   .
    / \
   b   .
      / \
     c   .
        / \
       d   nil

树不是简单的图形;孩子们被命令。

如果您说(a . b)是包含左子b和右子a的节点,则违反您正在使​​用的符号的约定,左右的含义,以及你正在使用的写作方向。

如果我们讨论Lisp,符号car - s打印在左侧,在列表的其余部分之前。混合起来并没有恰当地描述Lisp中的情况,我们必须考虑到所有内容,包括符号表达式和内部结构之间的对应关系。

如果您使用Lisp conses实现二进制数据结构,并且您不关心与Lisp的打印表示的任何对应关系,您可以左右分配carcdr无论哪种方式当然,你喜欢这个算法,算法也会解决。但是,代码中的约定并不描述Lisp语言。