LISP dynamically define functions

2019-06-11 11:56发布

问题:

I want to define a function with a parameter that defines another function with that parameter as the name.

example that dont work:

(DEFUN custom (name op const var)
  (DEFUN name (var) (op const var)))

The problem is that name isnt evaluated and so the function that is defined is always called name.

I know that FUNCALL and APPLY can evaluate parameter dynamically, but i don't know how to call FUNCALL DEFUN... correctly.

回答1:

I don't think you can use nested defuns.

You can either use a defun to return a lambda:

(defun custom (op const) 
  (lambda (arg) (funcall op const arg)))

and then use fdefinition:

(setf (fdefinition '+42) (custom '+ '42))

or use defmacro:

(defmacro custom (name op const)
  (let ((arg (gensym)))
    `(defun ,name (,arg)
       (,op ,const ,arg))))

(custom +42 + 42)

PS. I think you need to explain why you are trying to do this, then we will be able to explain your options better.



回答2:

It seems to me you might want to curry a function. Imagine you did this:

(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)

Now defun makes a function in the global namespace always. It's not like in Scheme where it creates a closure. To do the same as defun we use symbol-function and 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

Last. With macros you can prettify it.

(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 is taken from the lexical scope so you may use flet or labels with this but it ends up being global, just like with defun.



回答3:

The main problem is that DEFUN isn't a function, it's a macro that treats its arguments specially (specifically, it doesn't evaluate "the function name", "the argument list", not "the function body").

You could probably make something work by careful use of (setf (symbol-function ...) ...), something like:

(defun define-custom-function (name op const)
   (setf (symbol-function name) (lambda (var) (funcall op const var))))

This binds the function definition of the symbol to an anonymous function that calls your "operator" on your fed-in constant.

* (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

However, unless you absolutely need to define (or re-define) these custom functions dynamically, you are probably better off defining a custom DEFUN-like macro.