这段代码来自书:“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 ::标签 - FUNCTION-BODY)。
[类型的条件 CCL :: SIMPLE-PROGRAM-ERROR]
我不安静理解。需要帮忙。谢谢。
答案 0 :(得分:10)
中的
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 ...
将是一个提示(本地计算机上的调试器缓冲区)。
请查看Hyperspec中labels
的文档,了解详情。
答案 1 :(得分:7)
在其他语言中,不是Lisps,括号通常用于对运算符进行分组,因此在许多情况下都是可选的。但在Lisp中,括号总是有意义的。 不可能是额外的或可选的括号。
大多数情况下,表达式括号表示函数或宏应用程序:
(foo 1)
在这种情况下表达开始时可能出现两个括号,例如,当表达式的第一个元素是另一个表达式时,它被计算为函数本身。例如,假设函数make-adder
,它接受一个数字并返回另一个部分应用加法的函数(顺便说一下,它是currying的一个例子):
(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) ...)
所以,你可以看到,每个括号都意味着什么,你不能自己放弃它们。
答案 2 :(得分:3)
我认为有括号“(”没必要
他们是。您不能只是随意添加和删除括号,并期望它可以使用符号asdklfjhsbf
来代替defun
,并期望它可以工作。你必须给LABELS ((function-name lambda-list forms) ... )
,这只是LABELS的语法;如果你不遵循它,编译器将引发错误。
答案 3 :(得分:2)
圆括号在Lisp中非常重要。现在您可以理解为什么Land of Lisp music video说:“我早餐吃括号......如果我的程序不是我吃完午餐吃了...他们可能看起来很有趣但是他们有语义能力......这让你的节目充满了简洁和冲击。很快你也会对它们有梦想!
答案 4 :(得分:1)
正如@danlei所说,let
/ labels
/ flet
的第一部分必须有一个列表的函数定义。如果它是单个元素,那么你最终得到那些多余的双括号。
答案 5 :(得分:1)
确实有一些地方在Lisp中有些“不自然”。通常规则非常一致:括号开始列表,列表的第一个元素是要与所有剩余列表元素一起用作参数的函数。 对于大多数宏和特殊形式,这种一致性也得到了保持,这使得Lisp代码非常统一......但是在某些宏和特殊运算符括号中有不同的含义并用于分组...例如
(dolist (x L) (print x))
在这种情况下,例如第一个句子中的x
不是被称为传递L
作为参数的函数。另一个例子是
(let ((x 10)
(y 20))
(print (+ x y)))
在这种情况下,括号仅用于分组,第一个元素(x 10)
显然不是要应用的函数,也不是它的第一个元素x
。
labels
与let
完全相同,如果定义了一个函数,则需要明显的“额外”括号进行分组。
虽然这些特殊情况确实有些令人讨厌,但它们很少,在编写了一些Lisp代码之后,你会将它们内化,你只需不考虑就可以正确使用它们。
它们是不对称的,例如,编写一个正确的代码漫游器非常困难,而且当你在Lisp的这个“语法区域”中犯了一个错误时,不幸的是错误消息对你指向错误。
这种“复杂性”当然与其他语言相比毫无结果(当你在C ++模板中犯错误时,我们不会开始讨论消息的清晰程度;-))。
我认为Lisp语法“微不足道”甚至缺席都不完全正确。即使不是在字符级别,但在Lisp表单级别,Lisp语法仍然存在,并且除了少数特殊形式和宏之外,它的几乎微不足道。