Я немного не понимаю, «почему» вы пытаетесь сделать это с помощью defun
, но если вы хотите, чтобы пользователь мог определять свои собственные функции, а затем вызывать их, есть несколько способов сделать это.
1) если вы оцениваете его как обычную функцию defun
, вы можете просто сделать что-то вроде
(loop for f in *my-functions-names* and
for a in *my-functions-args* and
for b in *my-functions-bodies*
do (setf (symbol-function f) (compile `(lambda ,a ,b))))
тогда пользователи могли бы компилировать свои функции во время выполнения, и это выглядело бы так, как будто они были там все время.
2) создать хеш-таблицу лямбд и funcall
(или apply
) их функций:
(defparameter *user-functions* (make-hash-table))
(defun def-user-fun (f-name f-args f-body &optional (ns *user-functions*))
(setf (gethash f-name ns) (compile nil `(lambda ,f-args ,f-body))))
(defun call-user-fun (f-name args &optional (ns *user-functions*))
(funcall (gethash f-name *user-funcations) f-args))
Это позволит пользователям определять, какие функции они хотят, которые будут оцениваться в текущей среде.
3) если это просто для того, чтобы сэкономить ваше время при компиляции собственного кода, вы можете просто сделать то, что сделали, но с помощью defmacro и оператора цикла.
Обновить
Поскольку вам нужна рекурсия, вы можете сделать что-то похожее на то, что предложил @rainerjoswig с (lambda (args..) (labels ((recurse ...)) (recurse ...)))
. Хотя это может показаться нехорошим в императивном смысле, в Лиспе это идиоматично. Фактически, как компилятор SBCL, так и сборщик мусора настроены специально для такого рода рекурсии. Если вы хотите узнать об оптимизации Common Lisp, на которую вы можете положиться, прочтите стандарт.
(compile 'fname) == (setf (symbol-function 'fname) (compile nil (symbol-function 'fname)))
? Да и нет. По крайней мере, вероятно, не так, как вы думаете. Мне кажется, что вы запутались в двух вещах, обе из которых являются частью загадочного механизма "бэкенда" Лиспа.
Во-первых, symbol-function
не является оценщиком, он просто позволяет читатель лиспа знает, что этот символ является символом функции и должен быть доступен через среду функции, а не текущую лексическую среду, то есть, доступ к функции осуществляется как к переменной, но в другой среде или пространстве имен. Могу поспорить, что в фоновом режиме расширение labels
и есть эта функция.
Во-вторых, compile
также не работает таким образом. Вы можете либо передать ему имя symbol-function
, с помощью которого он получает доступ к определению хранимой функции, компилирует его, а затем старое определение с скомпилированным определением, (compile 'my-func)
, вы можете передать ему выражение lambda
, которое оно скомпилирует, (compile nil (lambda (args...) body))
или, наконец, вы можете передать как имя, так и определение, которое будет хранить скомпилированную функцию как эту функциональную переменную.
Так что да, в некотором смысле он расширяется до setf
, но не до вашего конкретного setf
, потому что вам не следует делать (compile nil (symbol-function 'fname))
.
Что касается разрешения рекурсии, то достаточно просто расширить @rainerjoswig, разрушив общую форму defun
, например
(def-user-func count-down (val)
(if (> val 0)
(progn (print val) (count-down (1- val)))
nil))
с таким макросом
(defmacro def-user-func (name args &body body)
`(compile ',name (lambda (,@args)
(labels ((,name (,@args)
,@body))
(,name ,@args)))))
Из-за того, что lisp скрывает имена переменных в пределах let
, labels
и других подобных утверждений, это будет работать отлично. «Внешний» мир просто видит функцию, в то время как функция видит только свою собственную функцию labels
. Когда ,@body
расширяется, это будет происходить в контексте labels
, и поскольку мы продублировали имена, это будет работать правильно.
person
Alex Hernandez
schedule
09.05.2017
(let ((name 'fn1) (body "abc")) (setf (symbol-function name) (compile nil `(lambda () ,body))))
или что-то в этом роде? clhs.lisp.se/Body/f_cmp.htm - person Will Ness   schedule 09.05.2017defun
о некоторых тонкостях, которыеsymbol-function
могут не учитывать (на основе комментариев Райнера Джосвига ниже). В моем приложении сконструированные функции определяются во время загрузки, так что, возможно,eval
не так уж и плох. - person davypough   schedule 10.05.2017