我是Lisp的新手,在业余时间慢慢学习......几个月前,我对Lisp REPL的错误报告感到困惑,以下表达式不起作用:
((if (> 2 1) + -) 1 2)
通过环顾四周然后我知道Lisp不是Scheme ...在Lisp中,我需要做任何一个:
(funcall (if (> 2 1) '+ '-) 2 1)
或
(funcall (if (> 2 1) #'+ #'-) 2 1)
我还看了一下关于lisp-1和lisp-2的介绍性材料,虽然我无法在那里完成所有的东西...无论如何,我知道quote
会阻止评估,因为评估规则的例外。
最近我正在阅读有关reduce
的内容......然后作为练习,我想编写自己的reduce版本。虽然我设法让它工作(至少看起来有效),但我意识到我仍然无法准确解释为什么在defun
的主体中需要某些地方funcall
,并且在某些地方不
以下是elisp中的myreduce
:
(defun myreduce (fn v lst)
(cond ((null lst) v)
((atom lst) (funcall fn v lst))
(t (funcall fn (car lst) (myreduce fn v (cdr lst))))))
(myreduce '+ 0 '(1 2 3 4))
我的问题是关于第3和第4行:
第3行:为什么我需要funcall
?为什么不只是(fn v lst)
?我的“论证”是在(fn v lst)
中,fn
是列表中的第一个元素,因此lisp可以使用此位置信息将其视为一个函数......但事实并非如此。所以我肯定错过了一些东西。
myreduce
的递归调用中的第4行:将fn
传递给myreduce
的递归调用? '+
或+
或其他什么?
我想应该有一些非常基本的东西我不知道...我想知道,当我按照第6行/最后一行所示调用myreduce
时,事后发生了什么(至少在如何传递'+
,有没有办法在任何REPL环境中跟踪它?
非常感谢,
/布鲁因
答案 0 :(得分:4)
Common Lisp是一个LISP-2,有两个名称空间。一个用于函数,一个用于变量。参数绑定在变量名称空间中,因此函数名称空间中不存在fn
。
(fn arg) ; call what fn is in the function namespace
(funcall fn ...) ; call a function referenced as a variable
'+
是一个符号,funcall
和apply
会在全局函数命名空间中查看它是符号而不是函数对象时查找它。 #'+
是(function +)
的缩写,它从本地函数名称空间解析函数。由于#'+
需要查找,因此大量调用'+
比'+
快。符号和函数都可以作为fn
传递给myreduce,传递的任何内容都与第4行传递的相同。
(myreduce '+ 0 '(1 2 3 4)) ; here funcall might lookup what '+ is every time (CLISP does it while SBLC caches it)
(myreduce #'+ 0 '(1 2 3 4)); here funcall will be given a function object looked up in the first call in all consecutive calls
现在,如果你传递'+
,它将被评估为+
并绑定到fn。
在myreduce
中,我们在递归中传递fn
,它也将被评估为+
。
对于#'+
,它评估函数并绑定到fn
。
在myreduce
中,我们在递归中传递fn
,它将被计算到变量名称空间中绑定的函数对象fn
。
Common Lisp具有添加到函数名称空间的构造。例如。
(flet ((double (x) (+ x x))) ; make double in the function namespace
(double 10)) ; ==> 20
但你可以编写它并在变量名称空间中使用它:
(let ((double #'(lambda (x) (+ x x)))) ; make double in the variable namespace
(funcall double 10))
答案 1 :(得分:2)
Common Lisp有两个(实际上超过两个)命名空间:一个用于变量,一个用于函数。这意味着一个名称可能意味着不同的东西,具体取决于上下文:它可以是一个变量,它可以是一个函数名称。
(let ((foo 42)) ; a variable FOO
(flet ((foo (n) (+ n 107))) ; a function FOO
(foo foo))) ; calling function FOO with the value of the variable FOO
如何定义变量的一些例子:
(defun foo (n) ...) ; n is a variable
(let ((n 3)) ...) ; n is a variable
(defparameter *n* 41) ; *n* is a variable
因此,无论何时定义和使用变量,名称都在变量名称空间中。
定义了函数:
(defun foo (n) ...) ; FOO is a function
(flet ((foo (n) ...)) ...) ; FOO is a function
因此,无论何时定义和使用函数,名称都在函数名称空间中。
由于函数本身是一个对象,因此可以将函数作为变量值。如果要调用这样的值,则需要使用FUNCALL或APPLY。
(let ((plus (function plus)))
(funcall plus 10 11))
现在为什么会这样? ; - )
示例:在Lisp-1中我无法写:
(defun list-me (list) (list list))
在Common Lisp中,上述代码没有冲突。
在通话(foo 42)
中,名称FOO只能是未定义的或它是一个功能。另一种替代方案不存在。所以在运行时我们永远不必检查FOO的函数值实际上是一个函数对象。如果FOO具有函数值,则它必须是函数对象。原因是:Common Lisp中不可能用函数来定义函数。
在Scheme中你可以写:
(let ((list 42))
(list 1 2 3 list))
上面需要在某个时候检查并导致错误,因为LIST是42,这不是一个函数。
在Common Lisp中,代码只定义了一个变量LIST,但函数LIST仍然可用。