【问题标题】:Why must I funcall a function returned from another?为什么我必须调用从另一个函数返回的函数?
【发布时间】:2016-08-26 23:55:47
【问题描述】:

为什么这不起作用?

( ((lambda () (lambda (x) (funcall #'1+ x)))) 2)
 ; yields Compile-time error: illegal function call

我遇到了这样的情况,后来发现funcall 修复了它,即

(funcall ((lambda () (lambda (x) (funcall #'1+ x)))) 2) ; => 3

我很困惑,因为看起来第一个应该可以工作,因为我实际上有一个我正在调用的函数,而不仅仅是一个可能属于任一命名空间的符号(即(type-of ((lambda () #'1+))) ; => FUNCTION)。我认为这有点像您不需要需要funcall 一个 lambda,例如((lambda (x) x) :HI) ; => :HI。我错过了什么?

【问题讨论】:

标签: common-lisp lisp-2


【解决方案1】:

Common Lisp 使用 form 这个词来表示所有可以评估的东西。 form

  • 一个符号,比如foo
  • 一个复合形式,一个列表,见下​​文
  • 自评估对象(如数字、字符、数组、字符串...)。

复合形式要么是

  • 一个特殊形式 (<special-operator> ...)
  • 一个 lambda 形式,例如 (lambda (...) ...)
  • 一个宏形式 (<macroname> ...)
  • 函数形式 (<functionname> ...).

上面是一组复合形式。 ANSI Common Lisp 规范没有提供添加新类型表单或不同语法的方法。形成EVALCOMPILEaccept等函数的接口是不可扩展的。

类似

(((lambda (foo)
    (lambda (bar)
      (list foo bar)))
  1)
 2)

不是有效的 Common Lisp。这在 Common Lisp 中没有意义:

( <not a lambda form,
   not a special operator,
   not a macro name
   and not a function name>
2)

注意,Common Lisp 允许 lambda 形式特殊运算符宏名函数名作为第一个复合形式的元素。但它确实不允许允许变量并且它确实不允许允许其他复合形式作为复合形式中的第一个元素.

表示这在 Common Lisp 中没有意义:

( <a function form> 2)

因此 ((foo 1) 2)(((foo 1) 2) 3)((((foo 1) 2) 3) 4)(((((foo 1) 2) 3) 4) 5) 在 Common Lisp 中是不合法的。你明白了。要调用函数调用返回的函数对象,我们必须使用(funcall (foo ...) ...)。这使得调用返回的函数对象比 ((foo ...) ...) 更明显。

让我们赞扬 Common Lisp 的设计者的这个功能。否则我可能不得不查看以

开头的可能有意义的代码
(((((((((((( .....

而且很难弄清楚它的作用。基本上那将是只写代码。

您的问题

为什么我必须调用从另一个函数返回的函数?

简短的回答:因为语法不允许其他方式,在 Common Lisp 中。

【讨论】:

    【解决方案2】:

    Common Lisp 的语法要求,每次你想通过以下类型的复合形式调用函数时:

    (f a1 a2 ... an)
    

    列表的第一个元素f,必须是表示函数名的符号,或表示lambda 表达式的列表,即(参见manual):

    lambda 表达式 n。一个 list 可以在某些上下文中用来代替 function name,通过直接描述其行为而不是通过引用间接来表示 function已建立的功能的名称;它的名字来源于它的第一个元素是 symbol lambda。

    因此,这基本上意味着您不能将任何返回函数作为值的表达式作为第一个元素。在这些情况下,您必须使用funcall

    因此,在您的第二个示例中,funcall 的第一个参数是 ((lambda () (lambda (x) (funcall #'1+ x)))),这是一个正确的复合形式,其中列表的第一个元素是 lambda 表达式 (lambda () (lambda (x) (funcall #'1+ x)))(应用于空参数列表)。

    在第一个示例中,作为列表的第一个元素,您有一个返回函数的表达式,因此您必须使用funcall

    【讨论】:

    • 哦,我明白了,非常感谢。除了 lambda 形式之外,是否还有另一种被归类为“lambda 表达式”的构造?我按照手册进行了操作,但与实践中的情况相比非常模糊。
    • 不,lambda 表达式是一个以符号 lambda 开头的列表,并且具有正确的语法。没有其他类型的 lambda 表达式。
    • @MasterMastic "Lambda 表达式" 仅适用于(lambda ...),但该语言还定义了lambda forms,在这些形式中,汽车是您可以使用的 lambda 表达式提到,例如((lambda (x) (+ 40 x)) 2) =&gt; 42
    【解决方案3】:

    我已经阅读了这个article然后在下面修改了我的代码:

    (defparameter *my-fun* 1)
    
    (defun my-func (v0)   
      (setf (symbol-function '*my-fun*)
        (lambda (v1)
          (+ v0 v1)))   
       '*my-fun*)
    

    并以这种方式调用它((my-func 2) 3),但它也报告“非法函数调用”。我认为我的代码符合 lambda 演算,但哪里错了。

    在我看来,(my-func 2)返回符号*my_fun*,而*my-fun*的函数单元指向一个函数对象,所以((my-func 2) 3) => (*my -fun* 3) => ((lambda (v1) (+ 2 v1)) 3) => (+ 2 3) => 5

    【讨论】:

      【解决方案4】:

      这行得通:首先你的定义相同:

      (defparameter *my-fun* 1)
      
      (defun my-func (v0)   
        (setf (symbol-function '*my-fun*)
          (lambda (v1)
            (+ v0 v1)))   
         '*my-fun*)
      

      但是,使用 funcall 调用:

      (funcall (my-func 2) 3)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多