![](/img/trans.png)
[英]Difference between #'callee and 'callee in Common Lisp
[英]What is the difference between FUNCALL and #'function-name in common lisp?
我正在阅读一本关于作业的书,我明白使用#'将变量视为函数而不是变量。 但我对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 "~%")))
我想我的困惑在于函数名称的确切含义。 当我读到这本书时,它说变量的值是函数对象。
#'function-name
是(function function-name)
。 没有任何东西被调用,评估与function-name
相关联的function-name
结果(表示函数的对象)。 funcall
用于调用函数。
使用两者的示例会话:
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
链接)。
Common Lisp中需要#'
和funcall
符号,因为这种语言是所谓的“Lisp-2”,其中给定的符号可以有两个独立funcall
相关的主要“含义”,通常列为
这些是近似解释,您将在以下示例中看到“表单的第一个元素”和“任何其他位置”不是正确的定义。
考虑例如:
上面的代码打印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) = □ ...}
而{ int (*p)(int) = □ ...}
(funcall p 12)
就像使用指针调用函数一样(与(*p)(12)
,“C”允许缩写为p(12)
)。
Common Lisp中令人困惑的部分是你可以在同一范围内同时拥有一个名为square
的函数和一个名为square
的变量,并且该变量不会隐藏该函数。 当您需要将变量的值用作函数或者您希望将函数用作值时,可以使用funcall
和function
两个工具。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.