我正在读一本关于作业的书,我明白使用#'将变量视为函数而不是变量。但我对FUNCALL有点朦胧。我理解lisp使变量成为对象,所以函数名称只是一个'指针'(可能是一个坏词,但希望你得到我的意思),在这种情况下你使用#'来调用它,或者是funcall调用它们的唯一方法是什么?离。
(defun plot (fn min max step)
(loop for i from min to max by step do
(loop repeat (funcall fn i) do (format t "*"))
(format t "~%")))
我不能这样做:
(defun plot (fn min max step)
(loop for i from min to max by step do
(loop repeat #'(fn i) do (format t "*"))
(format t "~%")))
我想我的困惑在于函数名称的确切含义。当我读到这本书时,它说变量的值就是函数对象。
答案 0 :(得分:13)
#'function-name
是(function function-name)
。没有任何东西被调用,评估与function-name
(表示函数的对象)相关联的函数的结果。 funcall
用于调用函数。
请参阅funcall中的function和HyperSpec。
使用两者的示例会话:
CL-USER> (defun square (x) (* x x))
SQUARE
CL-USER> #'square
#<FUNCTION SQUARE>
CL-USER> (function square)
#<FUNCTION SQUARE>
CL-USER> (funcall #'square 3)
9
CL-USER> (funcall 'square 3)
9
funcall
的第二次调用有效,因为它也接受一个符号作为函数指示符(有关详细信息,请参阅上面funcall
的链接)。
答案 1 :(得分:8)
Common Lisp中需要#'
和funcall
符号,因为这种语言是所谓的“Lisp-2”,其中给定符号可以具有两个独立且不相关的主要“含义”,通常列出如
这些是近似解释,您将在以下示例中看到“表单的第一个元素”和“任何其他位置”不是正确的定义。
考虑例如:
上面的代码打印144
......起初看起来可能会令人惊讶,但原因是同名square
使用了两个不同的含义:给定参数的函数返回结果将参数乘以自身和局部变量square乘以值12。
名称square
的第一次和第三次使用意思是名为square
的函数,我用红色绘制了名称。第二个和第四个用途是关于名为square
的变量,而是涂成蓝色。
Common Lisp如何决定哪个是哪个?关键是...... defun
之后的位置,在这种情况下,它显然是一个函数名,就像它是(square square)
第一部分中的函数名一样。同样作为let
形式内列表的第一个元素,它显然是变量名称,它也是(square square)
第二部分中的变量名称。
这看起来很精神病......不是吗?好吧,Lisp社区确实存在一些分歧,即这种双重含义是否会使事情更简单或更复杂,而且它是Common Lisp和Scheme之间的主要区别之一。
如果不深入细节,我只会说这个看起来很疯狂的选择是为了让Lisp宏更有用,提供足够的卫生,使它们能够很好地工作,而不会增加复杂性并消除完整卫生宏的表现力。肯定这是一个复杂的问题,这使得向学习它的人解释语言变得更加困难(这就是为什么Scheme被认为是一种更好(更简单)的教学语言)但是许多专家lispers认为这是一个很好的选择,使得Lisp语言变成了更好的解决实际问题的工具。
同样在人类语言中,语境无论如何都扮演着重要的角色,对于人类来说,这不是一个严重的问题,有时候同一个词可以用于不同的含义(例如,作为名词或动词,如“加州是州”我住在“或”陈述你的意见“)。
即使在Lisp-2中,您也需要将函数用作值,例如将它们作为参数传递或将它们存储到数据结构中,或者您需要将值用作函数,例如调用已接收的函数作为参数(您的情节案例)或已存储在某处。这是#'
和funcall
发挥作用的地方......
#'foo
确实只是(function foo)
的快捷方式,就像'x
(quote x)
的快捷方式一样。这个“函数”是一个特殊的形式,给定一个名称(在这种情况下为foo
)返回相关的函数作为一个值,你可以存储在变量中或传递:
(defvar *fn* #'square)
在上面的代码中,例如变量*fn*
将接收之前定义的函数。函数值可以像字符串或数字一样处理。
funcall
是相反的,允许调用函数不使用其名称但使用值...
(print (funcall *fn* 12))
上面的代码将显示144 ...因为现在正在调用存储在变量*fn*
中的函数作为参数传递12。
如果你知道“C”编程语言,那么类比考虑(let ((p #'square))...)
就像取square
函数的地址一样(与{ int (*p)(int) = □ ...}
一样),而(funcall p 12)
则是(*p)(12)
比如使用指针调用函数(与p(12)
一样,“C”允许缩写为square
)。
Common Lisp中令人困惑的部分是,您可以在同一范围内同时拥有名为square
的函数和名为funcall
的变量,并且该变量不会隐藏该函数。当您需要将变量的值用作函数或者您希望将函数用作值时,function
和{{1}}是可以使用的两个工具。