【问题标题】:LISP dynamically define functionsLISP 动态定义函数
【发布时间】:2014-04-09 23:18:14
【问题描述】:
我想定义一个带有参数的函数,该参数定义了另一个以该参数为名称的函数。
不起作用的例子:
(DEFUN custom (name op const var)
(DEFUN name (var) (op const var)))
问题是 name 没有被评估,所以定义的函数总是被称为 name。
我知道FUNCALL和APPLY可以动态评估参数,但我不知道如何正确调用FUNCALLDEFUN...。
【问题讨论】:
标签:
function
lisp
definition
【解决方案1】:
我认为你不能使用嵌套的 defuns。
您可以使用defun 来返回lambda:
(defun custom (op const)
(lambda (arg) (funcall op const arg)))
然后使用fdefinition:
(setf (fdefinition '+42) (custom '+ '42))
或使用defmacro:
(defmacro custom (name op const)
(let ((arg (gensym)))
`(defun ,name (,arg)
(,op ,const ,arg))))
(custom +42 + 42)
PS。我认为您需要解释为什么要这样做,然后我们才能更好地解释您的选择。
【解决方案2】:
在我看来,您可能想要 curry 一个函数。想象一下你这样做了:
(defun curry (op arg1)
(lambda (&rest args) (apply op (cons arg1 args))))
(funcall (curry #'+ 10) 20) ; ==> 30
(mapcar (curry #'+ 10) '(1 2 3 4)) ; ==> (11 12 13 14)
现在defun 总是在全局命名空间中创建一个函数。它不像在 Scheme 中创建一个闭包。与defun 一样,我们使用symbol-function 和setf:
(defun create-curried (name op arg1)
(setf (symbol-function name)
(lambda (&rest args) (apply op (cons arg1 args)))))
(create-curried '+x #'+ 10) ; ==> function
(+x 20) ; ==> 30
;; since it's a function, it even works with higher order functions
(mapcar create-curried '(+x -x /x *x) (list #'+ #'- #'/ #'*) '(10 10 10 10))
(/x 2) ; ==> 5
最后。使用宏可以美化它。
(defmacro defun-curried (newname oldname arg)
(if (and (symbolp newname) (symbolp oldname))
`(create-curried ',newname (function ,oldname) ,arg)
(error "Newname and Oldname need to be symbols")))
(defun-curried +xx + 20)
(+xx 10) ; ==> 30
oldname 取自词法范围,因此您可以使用 flet 或 labels,但它最终是全局的,就像使用 defun 一样。
【解决方案3】:
主要问题是DEFUN 不是一个函数,它是一个特殊处理其参数的宏(具体来说,它不评估“函数名”、“参数列表”,而不是“函数体")。
您可以通过仔细使用(setf (symbol-function ...) ...) 来完成某些工作,例如:
(defun define-custom-function (name op const)
(setf (symbol-function name) (lambda (var) (funcall op const var))))
这会将符号的函数定义绑定到一个匿名函数,该函数会在您的馈入常量上调用您的“运算符”。
* (defun define-custom-function (name op const)
(setf (symbol-function name) (lambda (var) (funcall op const var))))
DEFINE-CUSTOM-FUNCTION
* (define-custom-function 'add3 #'+ 3)
#<CLOSURE (LAMBDA (VAR) :IN DEFINE-CUSTOM-FUNCTION) {1002A4760B}>
* (add3 5)
8
但是,除非您绝对需要动态定义(或重新定义)这些自定义函数,否则您最好定义一个自定义的DEFUN-like 宏。