如何以函数作为参数递归调用函数

时间:2013-05-29 20:54:54

标签: emacs elisp

从问题How do I pass a function as a parameter to in elisp?我知道如何将函数作为参数传递给函数。 但是 我们需要更深入......

除了Lame电影引号之外,我想要一个函数,它将一个函数作为参数并且能够调用自身[再次传递它作为参数所使用的函数]。请考虑以下代码段:

(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

我原本预计会有四次调用伪,但func2的第二次迭代似乎已经失去了传递给第一次迭代的函数的知识(并从那里传递)。非常感谢任何帮助!

3 个答案:

答案 0 :(得分: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是一个来自fixed-point combinator s族的构造。粗略地说,这个想法是消除实现递归函数的需要(当你考虑如何在VM中编译/实现它们时,递归函数需要更多的复杂性)。 Y-combinator通过允许人们将所有函数机械地转换为非递归形式来解决这个问题,同时仍允许递归。

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

现在,Y-combinator的作用是什么:

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

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

  3. 返回无限数量参数的包装函数

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

  5. 这个结构还规定了与Y-combinator一起使用的函数的结构:它必须采用单个参数,它必须是一个函数(再次是同一个函数)并返回一个函数(任何数量的参数),它调用从外部范围继承的函数。

    嗯,众所周知,它有点令人难以置信:)

答案 1 :(得分:1)

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

(因此错误“符号的函数定义为void:func”。)

你想:

(func2 func (- arg 1)))

(func2 'func (- arg 1)))

答案 2 :(得分:1)

  1. 您无需在func来电
  2. 中引用func2
  3. 您错过了func2
  4. 中的递归终止条件

    这对我有用:

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