【问题标题】:What is the difference between FUNCALL and #'function-name in common lisp?common lisp中的FUNCALL和#'function-name有什么区别?
【发布时间】:2012-03-25 16:23:45
【问题描述】:

我正在阅读一本书以完成作业,并且我知道使用 #' 将变量视为函数而不是变量。但我对 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 common-lisp


    【解决方案1】:

    #'function-name(function function-name)。什么都没有被调用,评估任何一个结果都会导致与function-name(代表函数的对象)关联的函数。 funcall 用于调用函数。

    参见HyperSpec 中的funcallfunction

    使用两者的示例会话:

    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 链接)。

    【讨论】:

      【解决方案2】:

      Common Lisp 中需要 #'funcall 符号,因为这种语言是所谓的“Lisp-2”,其中给定符号可以有两个独立且不相关的主要“含义”,通常列为

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

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

      考虑例如:

      上面的代码打印出144... 乍一看可能会令人惊讶,但原因是同名square有两种不同的含义:给定参数的函数返回参数相乘的结果本身和值为 12 的局部变量 square。

      square 这个名字的第一次和第三次使用,意思是名为square函数,我把这个名字涂成了红色。第二个和第四个用途是关于一个名为square变量,并被涂成蓝色。

      Common Lisp 如何决定哪个是哪个?重点是位置...在defun 之后,在这种情况下显然是一个函数名,就像它是(square square) 第一部分中的函数名一样。同样,作为let 表单中列表的第一个元素,它显然是一个变量名,它也是(square square) 第二部分中的一个变量名。

      这看起来很精神病......不是吗?嗯,Lisp 社区确实存在一些分歧,即这种双重含义是否会使事情变得更简单或更复杂,这是 Common Lisp 和 Scheme 之间的主要区别之一。

      在不深入细节的情况下,我只想说这个明显疯狂的选择是为了让 Lisp 宏更有用,提供足够的卫生以使它们能够很好地工作,而不会增加复杂性和完全卫生宏的删除表达能力。当然,这是一个复杂的问题,使得向学习它的人解释语言变得更加困难(这就是为什么 Scheme 被认为是一种更好(更简单)的教学语言),但许多专业的 lisper 认为这是一个很好的选择,使 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) = &amp;square; ...} 一样),而不是 (funcall p 12) 就像使用指针调用函数(与(*p)(12) 一样,“C”允许缩写为p(12))。

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

      【讨论】:

      • 我认为如果你使用namespace这个词而不是模糊的词meaning,你的解释可能会不那么“神秘”。 CL 是一个 Lisp-2,这意味着一个符号可以出现在两个命名空间中,每个命名空间都有不同的值。即符号foo在值命名空间中可以有值42(你可以用symbol-value查看它的值),它也可以在函数命名空间(symbol-function)中有(lambda (x) (1+ x))的值。跨度>
      • 谢谢你。我很想知道。在您的情况下,您将函数传递给全局变量。如果你有这个功能,你不能也做 (*fn* 12) 吗?还是仅适用于作为函数存在的平方函数?
      • @Andy:(*fn* 12) 的形式在常见的 lisp 中表示“调用名为 *fn* 的函数,传递 12 作为参数”......被调用的函数在逻辑上是固定的,甚至可以内联由编译器:是函数(defun *fn* (x) ...)。以相同方式命名的变量是另一个不相关的东西,可以包含任何值……例如数字、字符串甚至函数的地址。 (funcall *fn* 12) 形式的意思是“调用地址包含在名为*fn* 的变量中的函数”。另一方面,#'square 或多或少意味着“函数‘square’的地址”。
      猜你喜欢
      • 2012-12-27
      • 2016-02-18
      • 2011-04-20
      • 2010-10-07
      • 2011-02-25
      • 2012-06-28
      • 2014-07-12
      • 1970-01-01
      相关资源
      最近更新 更多