首先,对我来说,这似乎与宏有关。我认为重点是您使用宏将(matrix-add-row '(1 2) '(3 4)) 转换为明确的总和列表,例如(list (+ 1 3) (+ 2 4))。
此外,您所写的内容有几个问题,您似乎不太了解反引号的工作原理。所以我认为最简单的帮助方法是为你解决一个例子。
由于这是家庭作业,我将解决一个不同(但相似)的问题。您应该能够接受答案并将其用于您的示例。假设我想解决以下问题:
编写一个宏diffs,它计算列表中连续元素对的所有差异。例如,
(diffs '(1 2 3)) 应扩展为 (list (- 2 1) (- 3 2)),然后计算结果为 (1 1)。
请注意,我的宏不会执行实际的减法运算,因此即使我在运行时之前不知道某些数字,我也可以使用它。 (我认为这类问题有点奇怪的原因是它确实需要在编译时知道列表的长度)。
我的解决方案将用作带有一个参数的宏,但如果我想使用递归,我还需要传入一个累加器,我可以从 nil 开始。所以我写了这样的东西:
(defmacro diffs (lst &optional accumulator)
...)
现在我该怎么处理lst?如果lst 是nil,我想触底并返回累加器,并在前面调用list,这将是列出我的列表的代码。像这样的:
(defmacro diffs (lst &optional accumulator)
(cond
((null lst)
;; You could write `(list ,@accumulator) instead, but that seems
;; unnecessarily obfuscated.
(cons 'list accumulator))
(t
(error "Aargh. Unhandled"))))
让我们试试吧!
CL-USER> (diffs nil)
NIL
不是很令人兴奋,但看起来很合理。现在使用macroexpand,它只是进行扩展而不进行评估:
CL-USER> (macroexpand '(diffs nil))
(LIST)
T
如果我们已经从递归中得到了一些东西呢?
CL-USER> (macroexpand '(diffs nil ((- a b) (- b c))))
(LIST (- A B) (- B C))
T
看起来不错!现在我们需要处理那里有实际列表的情况。您想要的测试是 consp 并且(对于我的示例)只有在至少有两个元素时才有意义。
(defmacro diffs (lst &optional accumulator)
(cond
;; A list of at least two elements
((and (consp lst) (consp (cdr lst)))
(list 'diffs (cdr lst)
(cons (list '- (cadr lst) (car lst)) accumulator)))
;; A list with at most one element
((listp lst)
(cons 'list accumulator))
(t
(error "Aargh. Unhandled"))))
这似乎几乎可以工作:
CL-USER> (macroexpand '(diffs (3 4 5)))
(LIST (- 5 4) (- 4 3))
T
但有两个问题:
- 列表倒退了
- 当我们实际构造递归展开时,代码有点可怕
让我们先使用反引号操作符修复第二部分:
(defmacro diffs (lst &optional accumulator)
(cond
;; A list of at least two elements
((and (consp lst) (consp (cdr lst)))
`(diffs ,(cdr lst)
,(cons `(- ,(cadr lst) ,(car lst)) accumulator)))
;; A list with at most one element
((listp lst)
(cons 'list accumulator))
(t
(error "Aargh. Unhandled"))))
嗯,实际上并没有短很多,但我认为它更清晰。
对于第二部分,我们可以继续将每个项目添加到累加器的末尾而不是前面,但这在 Lisp 中并不是特别快,因为列表是单独链接的。更好的是反向构造累加器,然后在最后反转它:
(defmacro diffs (lst &optional accumulator)
(cond
;; A list of at least two elements
((and (consp lst) (consp (cdr lst)))
`(diffs ,(cdr lst)
,(cons `(- ,(cadr lst) ,(car lst)) accumulator)))
;; A list with at most one element
((listp lst)
(cons 'list (reverse accumulator)))
(t
(error "Aargh. Unhandled"))))
现在我们得到:
CL-USER> (macroexpand '(diffs (3 4 5)))
(LIST (- 4 3) (- 5 4))
T
好多了!
最后两件事。首先,我的宏中仍然有一个错误子句。你能看到如何触发它吗?你能想出比仅仅输出错误更好的行为吗? (您的宏将不得不处理同样的问题)
其次,为了调试这样的递归宏,我建议使用macroexpand-1,它一次只展开一层。例如:
CL-USER> (macroexpand-1 '(diffs (3 4 5)))
(DIFFS (4 5) ((- 4 3)))
T
CL-USER> (macroexpand-1 *)
(DIFFS (5) ((- 5 4) (- 4 3)))
T
CL-USER> (macroexpand-1 *)
(LIST (- 4 3) (- 5 4))
T