I am practising writing macros in Elisp. The teacher suggested us to include the macro part of the program in "quasiquote".
I would like to write a macro "printCdrEach" that print each element of cdr of the list (Just for the sake of practicing)
(defmacro print-cdr-each (first &rest rest)
`(while (not (null ,rest))
(eval (car ,rest))
(setq rest (cdr ,rest)))
)
(print-cdr-each 1 2 3 4) ; Expected Output 2 3 4
I keep getting the error "Invalid function: 2". I am sure that the compiler thought 2
is a function. However, I am not sure how to fix this program.
Without changing the input format (printCdrEach x x x x)
, what is the appropriate use of "," in my program in order for it to work?
You should try macroexpand-1
:
(macroexpand-1 '(print-cdr-each 1 2 3 4))
; ==> (while (not (null (2 3 4)))
; (eval (car (2 3 4)))
; (setq rest (cdr (2 3 4))))
; ==> t
(2 3 4)
is invalid. Try it in the REPL and you get eval: 1 is not a function name
rest
several times so if it was an expression it would be evaluated more than once.There is really no need for a macro:
(defun for-each (operation &rest elements)
(loop :for e :in elements
:do (funcall operation e)))
(for-each #'print 10 20)
10
20
; ==> nil
Macros are syntax sugar. That means you should be able to say what
(print-cdr-each 1 2 3 4)
should expand to in code. Since you are doing it as a macro you may want to expand it to static:
(defmacro for-each (operation &rest elements)
`(progn ,@(loop :for e :in elements
:collect `(,operation ,e))))
(macroexpand-1 '(for-each print a b))
; ==> (progn (print a) (print b))
; ==> t
(let ((a 10) (b 20)) (for-each print a b))
10
20
; ==> 20