首页 > 解决方案 > 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)的情况下,在我的程序中正确使用“,”以使其正常工作是什么?

标签: macroslispelisp

解决方案


你应该尝试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

推荐阅读