从问题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
的第二次迭代似乎已经失去了传递给第一次迭代的函数的知识(并从那里传递)。非常感谢任何帮助!
答案 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的作用是什么:
将原始函数捕获到变量f
。
返回一个参数的包装函数,它将调用f
,当轮流调用时,由Y-combinator使用
返回无限数量参数的包装函数
调用原始函数传递它所调用的所有参数。
这个结构还规定了与Y-combinator一起使用的函数的结构:它必须采用单个参数,它必须是一个函数(再次是同一个函数)并返回一个函数(任何数量的参数),它调用从外部范围继承的函数。
嗯,众所周知,它有点令人难以置信:)
答案 1 :(得分:1)
那是因为你试图调用函数func
而不是函数dummy
。
(因此错误“符号的函数定义为void:func”。)
你想:
(func2 func (- arg 1)))
不
(func2 'func (- arg 1)))
答案 2 :(得分:1)
func
来电func2
func2
这对我有用:
(defun func2 (func arg)
(message "arg = %s" arg)
(funcall func)
(when (plusp arg)
(func2 func (- arg 1))))