Vector 公共Lisp中向量线性组合的计算

Vector 公共Lisp中向量线性组合的计算,vector,common-lisp,numerical-methods,sbcl,Vector,Common Lisp,Numerical Methods,Sbcl,我正在用Common Lisp进行一些数值计算,我需要用给定的数值系数计算几个向量的线性组合。我正在重写一段Fortran代码,这可以通过res=a1*vec1+a2*vec2+…+来完成一个*vecn。我对CL的最初理解是每次都写下如下内容: (map 'vector (lambda (x1 x2 ... xn) (+ (* x1 a1) (* x2 a2) ... (* xn an))) vec1 vec2 ... vecn) 但我很快注意到这种模式会一次又一次地重复出现

我正在用Common Lisp进行一些数值计算,我需要用给定的数值系数计算几个向量的线性组合。我正在重写一段Fortran代码,这可以通过
res=a1*vec1+a2*vec2+…+来完成一个*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))

我得到一个编译错误,本质上说
值向量不是LIST类型的。我不知道该怎么做。我觉得我错过了一些明显的东西。任何帮助都将不胜感激。

您已陷入文字陷阱。宏是语法重写,因此当在语法列表中传递3个文本向量时,可以在编译时对它们进行迭代,但是用列表的bindnig替换它是不同的。宏只能看到代码,它不知道在运行时执行操作时,
vectors
最终将绑定到什么。您或许应该将其改为函数:

(defun-vec-lin-com(系数向量和键(向量类型))
(应用#地图)
类型
(λ(&rest值)
(循环:对于系数:在系数中)
:对于值:在值中
:sum(*系数值)))
矢量)
现在您的初始测试将无法工作,因为您通过了语法而不是列表。您需要引用文字:

(vec-lin-com(101001000)(#(1234)#(456)#(789)))
; ==> #(7410 8520 9630)
(定义参数*系数*'(10 100 1000))
(定义参数*测试*'(#(1 2 3)#(4 5 6)#(7 8 9)))
(vec lin com*系数**测试*)
; ==> #(7410 8520 9630)

现在你可以把它变成一个宏,但是大部分的工作都是通过扩展来完成的,而不是宏,所以基本上你可以把宏扩展成类似于我函数的代码

请记住,宏是在编译时展开的,因此表达式
,@(mapcar#'(lambda(ca)(list'*ca))系数args)
必须在编译时有意义。在这种情况下,
mapcar
系数
args
获取的所有内容都是源代码中的符号
系数
向量

如果希望能够使用一组未知的参数(即编译时未知)调用
vec-lin-com
,则需要将其定义为函数。听起来您面临的主要问题是如何正确排列
+
的参数。使用
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))))

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

非常感谢@sylvester!这很好地解决了我的问题。谢谢。我没有注意到宏扩展在多大程度上是一种语法操作。我希望Lisp能够推断出我对宏的意图,而不需要坚实的类型系统基础。