为什么论点的立场在利弊上很重要?

问题描述

简单代码:

> (cons null (cons 1 2))
'(() 1 . 2)
> (cons (cons 1 2)  null)
'((1 . 2))

最初,我希望结果是相同的。我可以想到一些模糊的解释,但也想听听博学的人的观点。

为什么结果不同?

解决方法

按照数学的说法,某些运算是可交换的。加法是可交换的,因此(+ 1 2)(+ 2 1)的结果相同。除法不是可交换的; (/ 1 2)(/ 2 1)的结果不同。

OP对(cons null (cons 1 2))(cons null (cons 1 2))应该具有相同结果的期望实际上是对cons是可交换过程的期望。它不是。如果cons是可交换的,则(cons 1 2)(cons 2 1)是等效的。但是:

> (cons 1 2)
(1 . 2)
> (cons 2 1)
(2 . 1)

cons过程创建一个cons单元,该单元具有两个传统上称为carcdr的组件;它根据两个参数创建cons单元格。第一个参数放在单元格的car中,第二个参数放在单元格的cdr中。对于(cons 1 2),将1放置在car中,将2放置在cdr中;但对于(cons 2 1),2放置在car中,而1放置在cdr中。您可以看到(cons 1 2)(cons 2 1)必须有所不同,因为car单元格的cdrcons是不同的。

OP示例(cons null (cons 1 2))将空白列表放置在car单元格的cons中,并放置单元格(1 . 2)(它是由另一个对{{1}的调用创建的) })在该单元格的cons中。这是点缀列表(如下所述)cdr;这样的虚线列表通常在REPL中表示为(() . (1 . 2))

但是使用(() 1 . 2)时,将单元格(cons (cons 1 2) null)放置在(1 . 2)单元格的car中,并将空列表放在该位置的cons中单元格:cdr

现在,在Lisps中,列表是由((1 . 2) . ())个单元格组成的链,这些单元格以空列表结尾。像cons这样的链有时称为点缀列表不正确列表;这是一个(1 . (2 . 3))单元格链,该单元格不以空列表结尾。在REPL中,您经常会看到cons表示为(1 . (2 . 3))。另一方面,像(1 2 . 3)之类的链则称为适当列表;这是一个(1 . (2 . ()))单元格的链,它们确实以空列表终止。您会在REPL中将其表示为cons。通常,当某人使用不合格的术语 list 时,它们的意思是正确的列表

因此,OP代码(1 2)等同于包含单个成员((1 . 2) . ())的适当列表((1 . 2)),并且肯定与虚线列表(1 . 2)不同。

还有另一个过程(() 1 . 2),其作用与OP预期的一样。 append(append null (append '(1) '(2)))均得出(append (append '(1) '(2)) null)。最后两个表达式的计算结果相同,因为空列表是操作'(1 2) identity元素。也就是说,将非空项目列表与空列表结合起来会得到非空列表的副本。类似地,将一个空列表与一个非空列表连接在一起会返回该非空列表的副本。但是append不是可交换的; append返回一个列表,其中包含第一个列表的元素,后跟第二个列表的元素。因此,append-> (append '(1 2) '(3 4)),但是'(1 2 3 4)-> (append '(3 4) '(1 2))

还有另一个相关的数学属性:关联性。加法是关联的,即'(3 4 1 2)(+ 1 (+ 2 3))的计算结果相同。使用(+ (+ 1 2) 3)1 + (2 + 3)的中缀表示法都得出相同的结果。这有一个有趣的结果。由于分组并不重要,因此可以简单地将上面的中缀表达式写为(1 + 2) + 3。相应的前缀表达式为1 + 2 + 3,Lisps确实支持这种为多个操作数表达加法的方法。

(+ 1 2 3)关联的。由于cons根据其参数创建一个对,因此它不能是关联的。使用cons(cons 1 2)被创建。使用(1 . 2),将对(cons 1 (cons 2 3))放在另一对的(2 . 3)中,结果是cdr(在REPL中可能表示为(1 . (2 . 3)))。但是,使用(1 2 . 3)(cons (cons 1 2) 3)被创建并放置在另一对(1 . 2)中,从而产生car。请注意,由于((1 . 2) . 3)不具有关联性,因此cons之类的表达式没有意义;操作分组不清楚。

但是(cons 1 2 3) 关联的。使用append创建列表(append '(1) '(2))。使用(1 2)创建列表(append '(1) (append '(2) '(3))),然后将列表(2 3)(1)合并,得到(2 3)。使用(1 2 3)可以创建列表(append (append '(1) '(2)) '(3)),然后将其与列表(1 2)结合在一起,也将得到列表(3)。由于分组在这里并不重要,因此简单地编写(1 2 3)是有意义的,此外,Lisps确实支持这种表示附加操作的方式。

,

请参见SICP here中的“框和指针”图。 cons是成对的构造器-它只是将两件事放在一起。如果我们概括这种配对事物的概念,我们可以建造树。在方案/机架中,列表只是树的一种特殊情况(每个“左”分支保存列表的一个元素,每个右分支保存列表的其余部分)。

速记:由于编写(cons 1 (cons 2 (cons 3 null)))很麻烦,因此我们简化了:'(1 2 3)。注意,最后的null(或'())在缩写中被省略了。如果它不是null而是4,我们将得到以下简写:'(1 2 3 . 4)。点表示该结构的右侧不是列表。

对于第一个示例,null只是列表的元素-出现在左分支上。对于第二个示例,null是最右边的元素-表示列表的末尾。

    / \          Null is displayed as () in
   /   \         the shorthand version. Whenever
 null            a pair does not have a list
       / \       on the right,we see a dot.
      /   \ 
     1     2


       / \       Note here that the null
      /   \      is on the last right-most
          null   branch. This null is not
    / \          shown in the shorthand.
   /   \
  1     2

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...