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?

Ответ 1

Трудный вопрос, но, наконец, понял это. Проблема заключается в том, что #' в определении 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)

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

Ответ 2

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

Смотрите также этот вопрос: Как сделать закрытие в Emacs Lisp? и "solution" с помощью lexical-let. lexical-let является расширением для Emacs Lisp в пакете "cl".

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