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

And*_*ndy 5 function 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 "~%")))
Run Code Online (Sandbox Code Playgroud)

我不能这样做:

(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 "~%")))
Run Code Online (Sandbox Code Playgroud)

我想我的困惑在于函数名称的确切含义.当我读到这本书时,它说变量的值是函数对象.

Mir*_*anu 13

#'function-name(function 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
Run Code Online (Sandbox Code Playgroud)

第二次调用是funcall因为它也接受一个符号作为函数指示符(funcall有关详细信息,请参阅上面的链接).


650*_*502 8

#'funcall符号需要在Common Lisp的,因为这种语言是一种所谓的"Lisp的-2",其中一个给定的符号可以有两个独立的和无关的主要"意思"常列为

  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)
Run Code Online (Sandbox Code Playgroud)

例如,在上面的代码中,变量*fn*将接收之前定义的函数.函数值可以像任何其他值一样操作,如字符串或数字.

funcall 相反,允许调用函数不使用其名称,但使用值...

(print (funcall *fn* 12))
Run Code Online (Sandbox Code Playgroud)

上面的代码将显示144 ...因为*fn*现在变量中存储的函数被称为传递12作为参数.

如果您知道"C"编程语言,则类比正在考虑(let ((p #'square))...)使用函数的地址square(如同{ int (*p)(int) = &square; ...}),而是(funcall p 12)使用指针调用函数(因为(*p)(12)"C"允许缩写为p(12)).

Common Lisp中令人困惑的部分是你可以同时拥有一个名为的函数square和一个square在同一范围内命名的变量,并且该变量不会隐藏该函数.funcall并且function是当你需要分别使用一个变量的值作为函数或当你想一个函数作为一个值,你可以使用两个工具.

  • 如果你使用"命名空间"这个词而不是含糊的"含义"这个词,我认为你的解释可能不那么"神秘"了.CL是一个Lisp-2,这意味着符号可以出现在两个名称空间中,每个名称中都有不同的值.即符号`foo`可以在值命名空间中具有值42(您可以使用`symbol-value`查看其值),它也可以具有值`(lambda(x)(1+ x))`在函数命名空间(`symbol-function`)中. (4认同)