繁体   English   中英

常见的lisp中FUNCALL和#'函数名有什么区别?

[英]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用于调用函数。

请参阅HyperSpec中的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相关的主要“含义”,通常列为

  1. 当用作表单的第一个元素时,它表示一个函数
  2. 当在任何其他地方使用时,它意味着变量

这些是近似解释,您将在以下示例中看到“表单的第一个元素”和“任何其他位置”不是正确的定义。

考虑例如:

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) = &square; ...}{ int (*p)(int) = &square; ...} (funcall p 12)就像使用指针调用函数一样(与(*p)(12) ,“C”允许缩写为p(12) )。

Common Lisp中令人困惑的部分是你可以在同一范围内同时拥有一个名为square的函数和一个名为square的变量,并且该变量不会隐藏该函数。 当您需要将变量的值用作函数或者您希望将函数用作值时,可以使用funcallfunction两个工具。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM