在Scheme中,我如何理解“(define (fx . y) (cons xy))”

ked*_*bug 3 lisp scheme functional-programming

我是Scheme的新手,在这里我遇到了虚线列表的问题,这是一个例子:

(define (f x . y) (cons x y))

当我输入时: (f 1 2 3)结果是'(1 2 3)。是的,它返回一个列表,此时

x => 1y => '(2 3)

我的问题是,当f采用不固定长度的 args时,解释器如何知道这一点?

Jos*_*lor 5

我是Scheme的新手,在这里我遇到了虚线列表的问题,这是一个例子:

(define (f x . y) (cons x y))
Run Code Online (Sandbox Code Playgroud)

当我输入: (f 1 2 3) 结果是 '(1 2 3)。是的,它返回一个列表,此时

x => 1 and y => '(2 3).
Run Code Online (Sandbox Code Playgroud)

我的问题是,当 f 取不固定长度的 args 时,解释器怎么会知道这一点?

说它f需要一个不固定或任意数量的参数是不太正确的。它没有。它至少需要一个参数。如果你尝试调用(f),你会得到一个错误。理解这里发生了什么的关键可能是理解 Lisps 中的点对符号。它在另一个 Stack Overflow 问题(方案中的点符号)中有所涉及,但简短的版本是每个列表都是由成对构建的。一对可以写成(car . cdr)。例如,(cons 1 2) => (1 . 2)。一个列表 like(1 2 3)实际上是一对,其cdr另一对cdr是另一对,等等: (1 . (2 . (3 . ())))。因为读起来很尴尬,所以当我们写成对链时,我们通常会删除最后一个点以外的所有点,如果最后一个点cdr(),我们也省略了点和()。所以

(1 . (2 . (3 . 4))  === (1 2 3 . 4)
(1 . (2 . (3 . ())) === (1 2 3)
Run Code Online (Sandbox Code Playgroud)

请注意,虽然默认打印机不会以这种方式打印,但(1 2 3 4)也可以通过以下方式编写列表:

(1 . (2 3 4)) === (1 2 . (3 4)) === (1 2 3 . (4))
Run Code Online (Sandbox Code Playgroud)

虽然它不会那样打印,但您可以编写它,系统会理解它。例如,

> '(1 2 . (3 4))
(1 2 3 4)
Run Code Online (Sandbox Code Playgroud)

这可能是理解 lambda-list 符号的关键。当我们为函数编写 lambda 列表时,它代表一个函数,并且该 lambda 列表会根据参数进行解构。因此,如果我们有以下 lambda 列表并使用它们来解构参数列表(1 2 3),我们将得到结果绑定:

lambda-list    x             y      z
-------------------------------------
(x y z)        1             2      3
(x y . z)      1             2    (3)
(x . y)        1         (2 3)    n/a
x              (1 2 3)     n/a    n/a
Run Code Online (Sandbox Code Playgroud)

最后一种情况可能令人惊讶,但您可以测试所有这些实际上是否按预期工作:

((lambda (x y z)   (list x y z)) '(1 2 3)) => (1 2 3)
((lambda (x y . z) (list x y z)) '(1 2 3)) => (1 2 (3))
((lambda (x . y)   (list x y))   '(1 2 3)) => (1 (2 3))
((lambda x         (list x))     '(1 2 3)) => ((1 2 3))
Run Code Online (Sandbox Code Playgroud)

最后一个有点简洁,因为这意味着如果它不是语言的一部分,你可以这样做:

(define (list . elements) elements)
Run Code Online (Sandbox Code Playgroud)