在Common Lisp中计算向量的线性组合



我正在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)))

宏扩展表达式:

(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.我不知道如何处理这个问题。我觉得我错过了一些明显的东西。任何帮助将不胜感激。

你已经进入了文字陷阱。宏是语法重写,因此当您在语法列表中传递 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)

现在你可以把它做成一个宏,但大部分工作都是由扩展而不是宏完成的,所以基本上你的宏会扩展到与我的函数正在做的事情类似的代码。

请记住,宏是在编译时扩展的,因此表达式,@(mapcar #'(lambda (c a) (list '* c a)) coefficients args)在编译时必须有意义。 在这种情况下,mapcar获得的coefficientsargs的只是源代码中coefficientsvectors的符号。

如果您希望能够使用一组未知的参数(即编译时未知(调用vec-lin-com,则需要将其定义为函数。 听起来您遇到的主要问题是让参数+正确排序。 有一个技巧使用applymap来转置矩阵可能会有所帮助。

(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))))

这不是世界上最高效的代码;它做了很多不必要的协调。 但它是有效的,如果你发现这是一个瓶颈,你可以编写更有效的版本,包括一些可以利用编译时常量的版本。

最新更新