我正在使用Common Lisp进行一些数值计算,我需要计算具有给定数值系数的几个向量的线性组合。我正在重写一段Fortran代码,这可以通过res = a1*vec1 + a2*vec2 + ... + an*vecn
完成。我最初接受CL只是简单地写每次:
(map 'vector
(lambda (x1 x2 ... xn)
(+ (* x1 a1) (* x2 a2) ... (* xn an)))
vec1 vec2 ... vecn)
但我很快就注意到这种模式会一遍又一遍地重复出现,所以开始编写一些代码来抽象它。因为向量的数量以及因此lambda参数的数量会因地而异,所以我认为需要一个宏。我想出了以下内容:
(defmacro vec-lin-com (coefficients vectors &key (type 'vector))
(let ((args (loop for v in vectors collect (gensym))))
`(map ',type
(lambda ,args
(+ ,@(mapcar #'(lambda (c a) (list '* c a)) coefficients args)))
,@vectors)))
Macroexpanding表达式:
(vec-lin-com (10 100 1000) (#(1 2 3) #(4 5 6) #(7 8 9)))
产生看似正确的扩张:
(MAP 'VECTOR
(LAMBDA (#:G720 #:G721 #:G722)
(+ (* 10 #:G720) (* 100 #:G721) (* 1000 #:G722)))
#(1 2 3) #(4 5 6) #(7 8 9))
到目前为止,这么好...... 现在,当我尝试在这样的函数中使用它时:
(defun vector-linear-combination (coefficients vectors &key (type 'vector))
(vec-lin-com coefficients vectors :type type))
我收到一个编译错误,主要是The value VECTORS is not of type LIST
。我不知道如何处理这个问题。我觉得我错过了一些明显的东西。任何帮助将不胜感激。
答案 0 :(得分:6)
你已经进入了字面陷阱。宏是语法重写,因此当您在语法列表中传递3个文字向量时,您可以在编译时对它们进行迭代,但将其替换为对列表的bindnig则不一样。宏只能看到代码,它不知道什么vectors
最终将在运行时绑定到它的东西。你或许应该把它变成一个函数:
(defun vec-lin-com (coefficients vectors &key (type 'vector))
(apply #'map
type
(lambda (&rest values)
(loop :for coefficient :in coefficients
:for value :in values
:sum (* coefficient value)))
vectors))
现在,由于您传递了语法而不是列表,因此初始测试将无效。你需要引用文字:
(vec-lin-com '(10 100 1000) '(#(1 2 3) #(4 5 6) #(7 8 9)))
; ==> #(7410 8520 9630)
(defparameter *coefficients* '(10 100 1000))
(defparameter *test* '(#(1 2 3) #(4 5 6) #(7 8 9)))
(vec-lin-com *coefficients* *test*)
; ==> #(7410 8520 9630)
现在你可以把它变成一个宏,但是大部分工作都是由扩展而不是宏完成的,所以基本上你的宏会扩展到我的函数所做的类似代码。
答案 1 :(得分:3)
请记住,宏在编译时会被扩展,因此表达式,@(mapcar #'(lambda (c a) (list '* c a)) coefficients args)
必须在编译时有意义。在这种情况下,mapcar
得到coefficients
和args
的所有内容都是源代码中的符号coefficients
和vectors
。
如果您希望能够使用一组未知的参数调用vec-lin-com
(在编译时未知,那就是),您需要将其定义为函数。听起来你遇到的主要问题是让+
的参数正确排序。有一个trick使用apply
和map
来转置可能有用的矩阵。
(defun vec-lin-com (coefficients vectors)
(labels
((scale-vector (scalar vector)
(map 'vector #'(lambda (elt) (* scalar elt)) vector))
(add-vectors (vectors)
(apply #'map 'vector #'+ vectors)))
(let ((scaled-vectors (mapcar #'scale-vector coefficients vectors)))
(add-vectors scaled-vectors))))
这不是世界上最有效的代码;它做了很多不必要的工作。但它是有效的,如果你发现这是一个瓶颈,你可以编写更高效的版本,包括一些可以利用编译时常量的版本。