elisp functions as parameters and as return value

2020-02-28 04:34发布

I have the following code

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

A call

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

returns 55.0 (the correct value) in SBCL but crashes with the following stack in 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)

How can I make it work in Emacs lisp?

标签: lisp elisp
2条回答
太酷不给撩
2楼-- · 2020-02-28 05:05

A tricky question, but finally got this figured out. The problem is that #' in the definition of avg-damp makes the compiler compile the lambda function at the time when avg-damp itself is compiled, before the actual value of f is known. You need to delay the compilation of this function to a later point in time, when avg-damp is called, like this:

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

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

Backquoting does the trick.

Edit: Of course, the whole problem goes away if you define avg-damp in an uncurried form, such as this:

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

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

But I guess you have your reasons not to do so.

查看更多
Deceive 欺骗
3楼-- · 2020-02-28 05:27

This style of programming does not work in plain Emacs Lisp. Emacs Lisp uses dynamic binding and languages like Scheme and Common Lisp are using lexical binding. Your code exposes the difference. See: Extent in Emacs Lisp

See also this question: How do I do closures in Emacs Lisp? and the 'solution' with lexical-let. lexical-let is an extension for Emacs Lisp in the "cl" package.

See also: since Emacs 24.1 there is optional lexical binding. Learn how to use it: using lexical binding.

查看更多
登录 后发表回答