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

时间:2012-03-08 05:39:52

标签: 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 "~%")))

我不能这样做:

(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 "~%")))

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

2 个答案:

答案 0 :(得分:13)

#'function-name(function function-name)。没有任何东西被调用,评估与function-name(表示函数的对象)相关联的函数的结果。 funcall用于调用函数。

请参阅funcall中的functionHyperSpec

使用两者的示例会话:

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”,其中给定符号可以具有两个独立且不相关的主要“含义”,通常列出如

  1. 当用作表单的第一个元素时,它表示函数
  2. 在任何其他地方使用时,它表示变量
  3. 这些是近似解释,您将在以下示例中看到“表单的第一个元素”和“任何其他位置”不是正确的定义。

    考虑例如:

    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; ...}一样),而(funcall p 12)则是(*p)(12)比如使用指针调用函数(与p(12)一样,“C”允许缩写为square)。

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