функции elisp как параметры и как возвращаемое значение

у меня есть следующий код

(defun avg-damp(f) 
    #'(lambda(x) (/ (+ (funcall f x) x) 2.0)))

вызов

(funcall (avg-damp #'(lambda(v) (* v v))) 10)

возвращает 55.0 (правильное значение) в SBCL, но аварийно завершает работу со следующим стеком в emacs lisp

Debugger entered--Lisp error: (void-variable f)
  (funcall f x)
  (+ (funcall f x) x)
  (/ (+ (funcall f x) x) 2.0)
  (lambda (x) (/ (+ ... x) 2.0))(10)
  funcall((lambda (x) (/ (+ ... x) 2.0)) 10)
  eval((funcall (avg-damp (function ...)) 10))
  eval-last-sexp-1(nil)
  eval-last-sexp(nil)
  call-interactively(eval-last-sexp)

как я могу заставить его работать в Emacs lisp?

2 ответов


сложный вопрос, но, наконец, понял это. Проблема в том, что #' в определении avg-damp компилятор компилирует лямбда-функцию в момент когда avg-damp сам компилируется, прежде чем будет известно фактическое значение f. Вам нужно отложить компиляцию этой функции на более поздний момент времени,когда avg-damp вызывается, например:

(defun avg-damp (f)
   `(lambda(x) (/ (+ (funcall ,f x) x) 2.0)))

(funcall (avg-damp #'(lambda(v) (* v v))) 10)

Backquoting делает трюк.

редактировать: Конечно, вся проблема уходит, если вы определяете avg-damp в неторопливой форме, например:

(defun avg-damp (f x)
   (/ (+ (funcall f x) x) 2.0))

(funcall 'avg-damp #'(lambda(v) (* v v)) 10)

но я думаю, у вас есть причины не делать этого.


этот стиль программирования не работает в обычном Emacs Lisp. Emacs Lisp использует динамическую привязку, а такие языки, как Scheme и Common Lisp, используют лексическую привязку. Ваш код раскрывает разницу. См.: экстент в Emacs Lisp

см. Также этот вопрос: как сделать закрытие в Emacs Lisp? и "решение" с лексическим-пусть. lexical-let является расширением для Emacs Lisp в пакете "cl".

см. также: поскольку Emacs 24.1 существует необязательно лексические привязки. Узнайте, как его использовать:использование лексической привязки.