8

这段代码来自书:“Land Of Lisp”第一个版本来自书。当我阅读它时,我认为在第二行的“at-loc-p”和第三行的 loc 之后有括号“(”不是必需的。

(defun person-at (loc pers per-locs)
       (labels ((at-loc-p (pers)
                 (eq (cadr (assoc pers per-locs)) loc)))
         (remove-if-not #'at-loc-p pers)))

但是当我测试这个时,

(defun person-at (loc pers per-locs)
       (labels (at-loc-p (pers)
                 (eq (cadr (assoc pers per-locs)) loc))
         (remove-if-not #'at-loc-p pers)))

结果出来了:

AT-LOC-P 中的必​​需参数与 lambda 列表 (CCL::FUNCNAME CCL::LAMBDA-LIST &BODY CCL::LABELS-FUNCTION-BODY) 不匹配。
[CCL::SIMPLE-PROGRAM-ERROR 类型的条件]

我不太明白。需要帮忙。谢谢你。

4

6 回答 6

11

LABELS在_

(defun person-at (loc pers per-locs)
  (labels ((at-loc-p (pers)
             (eq (cadr (assoc pers per-locs)) loc)))
    (remove-if-not #'at-loc-p pers)))

具有语法labels ((function-name lambda-list [[local-declaration* | local-documentation]] local-form*)*) declaration* form*,因此您必须提供本地函数定义列表才能使其工作。

由于这些本地函数定义本身是带括号的,因此您必须传递labels此结构的列表:((fun1 (...) ...) (fun2 (...) ...) ...).

不幸的是,堆栈跟踪和错误消息对于在这里发现错误并没有多大帮助,因为该消息没有告诉您问题出在labels,而且它也不是跟踪中的最顶层。这4: (CCL::NX1-LABELS ...将是一个提示(我本地机器上的调试器缓冲区)。

查看Hyperspeclabels中的文档以了解更多信息。

于 2011-05-08T02:20:57.527 回答
7

在其他语言中,不是 Lisps,括号通常用于对运算符进行分组,因此在许多情况下是可选的。但在 Lisp 中,括号总是有意义的。可能没有额外的或可选的括号。

最常见的围绕表达式的括号表示函数或宏应用程序

(foo 1)

在这种情况下,表达式开头可能会出现两个括号,例如,当表达式的第一个元素是另一个表达式时,它会被评估为函数本身。例如,想象 function ,它接受一个数字并返回另一个带有部分加法的函数(顺便说一句,这是curryingmake-adder的一个例子):

(defun make-adder (number)
   (lambda (another-number) (+ number another-number)))

我们可以通过这种方式创建函数变量increment,然后将其应用于变量:

(defvar increment (make-adder 1))
(increment 5)                      ; ==> 6

但是我们也可以直接调用它(好吧,这在 Common Lisp 中不起作用,但在其他 Lisp 中也可以使用相同的语法,称为"Lisp-1",所以我相信这里值得一提):

((make-adder 1) 5)                 ; ==> 6

在开头做双括号。而且,当然,两个括号都是强制性的。

最后一种情况,描述了您的情况,是语言或用户宏出于其目的使用列表列表(您还记得,Lisp 程序本身就是表达式列表的列表吗?)。例如,defun知道它的第一个参数必须是一个符号,它的第二个参数必须是一个列表。而且labels宏知道,它的第一个参数必须是一个定义列表,每个定义本身就是一个列表。它允许用户一次定义多个标签:

(labels ((definition-1) (definition-2) ...) 
   (do-this) (do-that) ...)

所以,你可以看到,每个括号都意味着什么,你不能自己删除它们。

于 2011-05-08T02:54:58.247 回答
3

我认为有括号“(”不是必需的

嗯,他们是。您不能随心所欲地添加和删除括号,并期望它能够工作,就像您可以使用符号asdklfjhsbf代替,比如说,defun并期望它能够工作一样。你必须给 LABELS ((function-name lambda-list forms) ... ),这只是 LABELS 的语法;如果您不遵循它,编译器将引发错误。

于 2011-05-08T02:17:43.297 回答
2

括号在 Lisp 中非常重要且必不可少。现在你可能明白为什么Lisp 的音乐视频说:“我早餐吃括号......如果我的程序没有完成,我午餐吃括号......它们可能看起来很有趣,但它们具有语义能力......这使您的程序更加简洁和有力。很快您也会对它们产生梦想!

于 2011-05-08T19:57:39.037 回答
2

确实,在 Lisp 中,有些地方的泛化有点“不自然”。通常规则是非常一致的:括号开始一个列表,列表的第一个元素是要使用的函数,所有剩余的列表元素作为参数。大多数宏和特殊形式也保持这种一致性,这使得 Lisp 代码非常统一......但是在某些宏和特殊运算符中括号具有不同的含义并用于分组......例如

(dolist (x L) (print x))

在这种情况下,例如x在第一个 panthesis 中不是L作为参数传递的函数。另一个例子是

(let ((x 10)
      (y 20))
   (print (+ x y)))

在这种情况下,括号仅用于分组,第一个元素(x 10)显然不是要应用的函数,它的第一个元素也不是x函数。

labels工作原理完全一样let,如果定义了多个函数,则分组需要明显的“额外”括号。

虽然这些特殊情况确实有些烦人,但它们很少,并且在编写了一些 Lisp 代码之后,您将内化它们,并且您会不假思索地正确处理它们。

它们仍然是不对称的,例如很难编写正确的代码遍历器,而且当你在 Lisp 的这个“语法区域”中犯了错误时,不幸的是,错误消息并不能真正帮助你指出错误。

与其他语言相比,这种“复杂性”当然是微不足道的(我们不要开始讨论当您在 C++ 模板中犯错时消息的清晰程度;-))。

我认为 Lisp 语法“微不足道”甚至不存在的说法并不完全正确。即使不是在字符级别而是在 Lisp 表单级别也存在 Lisp 语法,并且排除少数特殊形式和宏几乎是微不足道的。

于 2011-05-08T06:38:14.183 回答
1

正如@danlei 所说, a / /的第一部分必须有一个函数定义列表。如果它是单个元素,那么您最终会得到那些看起来多余的双括号。 letlabelsflet

于 2011-05-08T02:30:09.510 回答