2

I am taking efforts to rephrase an example from SICP

 #+BEGIN_SRC scheme
(define (average-damp f)
  (lambda (x) (average x (f x))))
(define (average x y)
  (/ (+ x y) 2))
(define (square x)
  (* x x))
((average-damp square) 10)
#+END_SRC

#+RESULTS:
: 55

After spent hours on it and got

(defun average-damp (f)
  (lambda (x) (average x (funcall f x))))

(defun average (x y)
  ;; Keep the float
  (/ (+ x y) 2.0))

(defun square (x)
  (* x x))

(funcall #'(average-damp 'square) 10)

Still report error invalid function, yet I am lost

Could you please provide any hints?

1
  • To learn about Emacs Lisp, do M-: (info "(eintr) Top"), or do C-h i followed by choosing Emacs Lisp Intro. See also Learn Emacs Lisp on EmacsWiki. You will spend less time that way, I predict.
    – Drew
    Commented Nov 1, 2019 at 17:07

3 Answers 3

6

You are almost there. There are only two little details you need to take care of:

  1. Remove the quoting #' in (funcall #'(average-damp 'square) 10). You do want to eval average-damp and give you the actual function for the funcall. That is what Aquaactress correctly statet as problem 1.

  2. For the form

       (defun average-damp (f)
         (lambda (x) (average x (funcall f x))))
    

    to work you need lexical binding. The argument list of average-damp goes into its lexical environment. You can refer to that variable in the lambda that is within the lexical environment of average-damp. So your source block defining average-damp should have the :lexical t header argument. It should look like follows:

    #+begin_src emacs-lisp :lexical t
       (defun average-damp (f)
         (lambda (x) (average x (funcall f x))))
    #+end_src
    
4
  • ty, why both ` #'square` and 'square works in (funcall (average-damp #'square) 10) ?
    – Wizard
    Commented Nov 1, 2019 at 17:25
  • 1
    @Algebra That is one of the specialities of Elisp. Elisp does not have a special obarray for function symbols. Instead each symbol has a function cell beside the other stuff like the value cell and the plist. Thus, #' just stands for the ordinary quote. Nevertheless it is recommended to use #' since Elisp checkers can warn if the quoted symbol does not have a function definition.
    – Tobias
    Commented Nov 1, 2019 at 17:41
  • Could I consult that is it a good practice that while on one hand exploiting SICP, on the other hand learning Elisp by replace scheme with elisp? I doubt that scheme might be very fluent and powerful in prototype thinking without interrupting by the detailed implementations.
    – Wizard
    Commented Nov 1, 2019 at 17:50
  • 1
    I assume Drew has already answered the question in your comment. Certainly you can use SICP to learn the principles but I would use Scheme to try the programs. Maybe, translating Scheme to Elisp is a good one-time excersize but not for the examples of a whole book.
    – Tobias
    Commented Nov 1, 2019 at 18:02
4

There are two problems I found with the code you wrote.

problem 1

(funcall #'(average-damp 'square) 10)

funcall requires a function as it's first argument. However, here you're trying to pass in the unevaluated list (average-damp 'square) as if it were a function. But it's not, it's data.

Remember that #' is a shorthand for function and function does not evaluate it's arguments (see anonymous functions). So it does not evaluate (average-damp 'square).

problem 2

The other problem is your definition of average-damp.

(defun average-damp (f x)
  (lambda (x) (average x (funcall f x))))

lambda does not evaluate it's body. Therefore the f that you're passing into average-damp won't end up replacing the f in (funcall f x) which it seems like is what you want.

As an illustration, this is what your version of average-damp returns when passed in with square.

(average-damp 'square) ;=> (lambda (x) (average x (funcall f x)))

Note that the f still hasn't been replaced with square.

Consequently, when the lambda this function is called, it won't know what f is (unless you defined a global variable f) and you're bound to get a Symbol's value as variable is void: f error.

solution

To address problem 2 you can use backquote to ensure that the value of average-damp's parameter, f, is replaced with square.

And to address problem 1, you should remove the #'. You don't need it because you want (average-damp 'square) to be evaluated so that it returns a function, which is what funcall requires as it's first argument.

(defun average-damp (f)
  `(lambda (x) (average x (funcall #',f x))))

(funcall (average-damp 'square) 10) ;=> 55.0
2
  • Note that the backquoting in your solution to problem 2 is old-style. Nowadays one uses lexical binding. See my answer. Motivation: If the question originates from an effort to transform a Scheme library into an Emacs library the coding style guidelines for Emacs libraries require lexical-binding anyway. So average-damp is already a closure in such a library.
    – Tobias
    Commented Nov 1, 2019 at 17:13
  • Thanks for mentioning that. I upvoted your answer. Commented Nov 1, 2019 at 17:21
1

You can not enter x value to (average-damp ) in your code.

You should declare x as a function argument or define (average-damp ) as a macro.

(defun average-damp (f x)
  (average x (funcall f x)))

(defmacro average-damp2 (f)
  `(lambda (x) (average x (funcall ,f x))))

(defun average (x y)
  ;; Keep the float
  (/ (+ x y) 2.0))

(defun square (x)
  (* x x))

;; using function
(funcall #'average-damp #'square 10);; => 55.0
(average-damp #'square 10);; => 55.0

;; using macro
((lambda (x) (average x (square x))) 10);; => 55.0
(funcall (average-damp2 #'square) 10);; => 55.0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.