Lisp - 在宏中使用逗号 (`,`)

问题描述

我正在练习用 Elisp 编写宏。老师建议我们将程序的宏部分包含在“quasiquote”中。

我想写一个宏“printCdrEach”来打印列表中cdr的每个元素(仅供练习)

(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

我不断收到错误“无效函数:2”。我确信编译器认为 2一个函数。但是,我不确定如何修复此程序。

在不更改输入格式 (printCdrEach x x x x) 的情况下,在我的程序中适当使用“,”以使其正常工作?

解决方法

你应该试试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) 无效。在 REPL 中试试,你会得到 eval: 1 is not a function name
  • 您多次使用 rest,因此如果它是一个表达式,它将被计算多次。

真的不需要宏:

(defun for-each (operation &rest elements)
  (loop :for e :in elements
        :do (funcall operation e)))

(for-each #'print 10 20)
10
20
; ==> nil

宏是语法糖。这意味着你应该能够说什么 (print-cdr-each 1 2 3 4) 应该在代码中扩展为。由于您将其作为宏执行,因此您可能希望将其扩展为静态:

(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