0

来自问题如何将函数作为参数传递给 elisp?我知道如何将函数作为参数传递给函数。 我们需要更深入...

撇开蹩脚的电影引用不谈,我想要一个函数,它将函数作为参数并能够调用自己[再次传递它作为参数的函数]。考虑这个片段:

(defun dummy ()
  (message "Dummy"))

(defun func1 (func)
  (funcall func))

(defun func2 (func arg)
  (message "arg = %s" arg)
  (funcall func)
  (func2 'func (- arg 1)))

调用(func1 'dummy)会产生预期的输出:

Dummy    
"Dummy"

调用(func2 'dummy 4)会导致错误消息:

arg = 4
Dummy
arg = 3
funcall: Symbol's function definition is void: func

我曾预计对 dummy 的四次调用,但第二次迭代func2似乎已经失去了传递给第一次迭代(并从那里传递)的函数的知识。任何帮助深表感谢!

4

3 回答 3

4

使用词法作用域可能有更好的方法来做到这一点。这或多或少是来自 Rosetta Code 的翻译:

(defun closure (y)
  `(lambda (&rest args) (apply (funcall ',y ',y) args)))

(defun Y (f)
  ((lambda (x) (funcall x x))
   `(lambda (y) (funcall ',f (closure y)))))

(defun factorial (f) 
  `(lambda (n)
     (if (zerop n) 1 
       (* n (funcall ,f (1- n))))))

(funcall (Y 'factorial) 5) ;; 120

这是 Rosetta 代码的链接:http : //rosettacode.org/wiki/Y_combinator 以及许多其他实现相同内容的语言。Y-combinator 是一个结构,来自定点组合器s 系列。粗略地说,这个想法是为了消除实现递归函数的需要(当你考虑如何在 VM 中编译/实现递归函数时,它们需要更多的复杂性)。Y-combinator 通过允许将所有函数机械地转换为非递归形式来解决这个问题,同时仍然允许一般的递归。

公平地说,上面的代码不是很好,因为它会在每个递归步骤中创建新函数。这是因为直到最近,Emacs Lisp 还没有词法绑定(你不能让函数捕获它的词法环境),换句话说,当 Emacs Lisp 函数在它声明的范围之外使用时,绑定变量将从函数的当前范围中获取。在上述情况下,此类绑定变量fY函数和函数yclosure。幸运的是,这些只是指定现有功能的符号,因此可以使用宏来模仿这种行为。

现在,Y-combinator 做了什么:

  1. 将原始函数捕获到变量f中。

  2. 返回一个参数的包装函数,该函数将调用f,当依次调用时,Y-combinator 使用它

  3. 返回一个包含无限数量参数的包装函数,它将

  4. 调用原始函数,将调用它的所有参数传递给它。

此结构还规定了要与 Y-combinator 一起使用的函数的结构:它必须接受单个参数,该参数必须是一个函数(再次是同一个函数)并返回一个函数(具有任意数量的参数)调用从外部作用域继承的函数。

好吧,众所周知,这有点令人难以置信:)

于 2013-05-29T21:41:32.497 回答
1
  1. 您无需funcfunc2通话中报价
  2. 您在中缺少递归终止条件func2

这对我有用:

(defun func2 (func arg)
  (message "arg = %s" arg)
  (funcall func)
  (when (plusp arg)
    (func2 func (- arg 1))))
于 2013-05-29T21:27:48.997 回答
1

那是因为您试图调用函数func而不是函数dummy

(因此出现错误“Symbol 的函数定义为 void: func”。)

你要:

(func2 func (- arg 1)))

不是:

(func2 'func (- arg 1)))
于 2013-05-29T21:24:55.537 回答