Вычисление линейной комбинации векторов в Common Lisp - PullRequest
0 голосов
/ 13 мая 2018

Я работаю над некоторыми числовыми вычислениями в Common Lisp, и мне нужно вычислить линейную комбинацию нескольких векторов с заданными числовыми коэффициентами. Я переписываю часть кода на Фортране, где это может быть достигнуто res = a1*vec1 + a2*vec2 + ... + an*vecn. Первоначально я хотел просто написать каждый раз что-то вроде:

(map 'vector 
  (lambda (x1 x2 ... xn)
    (+ (* x1 a1) (* x2 a2) ... (* xn an)))
  vec1 vec2 ... vecn)

Но вскоре я заметил, что этот шаблон будет повторяться снова и снова, и поэтому начал писать некоторый код, чтобы абстрагировать его. Поскольку число векторов и, следовательно, количество аргументов лямбды будет варьироваться от места к месту, я подумал, что потребуется макрос. Я придумал следующее:

(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. Я не уверен, как подойти к этому. Я чувствую, что упускаю что-то очевидное. Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Помните, что макросы раскрываются во время компиляции, поэтому выражение ,@(mapcar #'(lambda (c a) (list '* c a)) coefficients args) должно быть значимым во время компиляции.В этом случае все, что mapcar получает для coefficients и args, это символы coefficients и vectors из исходного кода.

Если вы хотите иметь возможность вызвать 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))))

Это не самый эффективный код в мире;это делает много ненужного.Но это эффективно, и если вы сочтете это узким местом, вы можете написать более эффективные версии, в том числе те, которые могут использовать преимущества констант времени компиляции.

0 голосов
/ 14 мая 2018

Вы попали в буквальную ловушку. Макросы переписывают синтаксис, поэтому, когда вы передаете 3 литеральных вектора в список синтаксиса, вы можете выполнять итерации по ним во время компиляции, но замена его привязкой к списку - это не то же самое. Макрос только видит код и не знает, с чем 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)

Теперь вы можете сделать это макросом, но большая часть работы была бы сделана расширением, а не макросом, так что в основном ваш макрос расширился бы до кода, аналогичного тому, что делает моя функция.

...