质心给出了一个向量列表

时间:2016-08-30 08:17:41

标签: vector lisp common-lisp

我必须计算给定一个向量列表的平均向量,例如这个

'((2 3 56) (22 45 34) (21 2 23) (4 8 3) (4 4 1) (4 4 5))

简而言之,我必须找到列表列表中的质心。

(defun vsum (x y)
 (cond ((not (= (list-length x) (list-length y))) (error "dimension error!"))
   ((null (first x)) NIL)
       (t (cons (+ (first x) (first y)) (vsum (rest x) (rest y))))))

我已经创建了这个简单的函数,但是在以递归的方式使用它(我更喜欢它反对循环)来完成我的任务时遇到了很大的麻烦。我也需要它与维度无关(例如,大多数为2或3的向量)。

2 个答案:

答案 0 :(得分:3)

在这种情况下,不需要循环或递归,只需要原始函数:

(defun centroid (list)
  (when list
    (let ((list-length (length list))
          (dimension (length (first list))))
      (unless (every (lambda (v) (= (length v) dimension)) (rest list))
        (error "Dimension error!"))
      (mapcar (lambda (x) (/ x list-length))
              (reduce (lambda (x y) (mapcar #'+ x y)) list)))))

使用的公式是有限的一组点(见Wikipedia)。

首先检查所有向量是否具有相同的维度(具有every的部分),然后使用(reduce (lambda (x y) (mapcar #'+ x y)) list)部分计算总和,最后每个坐标除以点数(mapcar部分)。

答案 1 :(得分:3)

  

我更倾向于反对循环

但这没有任何意义。递归函数更难使用,并且可能导致堆栈溢出。

您的vsum函数更好地写为

(defun vsum (x y)
  (assert (= (length x) (length y))   ; both lists of equal length
      (x y)                           ; the lists, can be repaired
    "Dimension error")                ; the error message
  (mapcar #'+ x y))                   ; simple mapping

以上版本是

  • 由于使用ASSERT
  • ,最好在发生错误时以交互方式使用
  • 更明确
  • 较大的输入列表
  • 没有堆栈溢出问题

mapcar表达式可以使用loop编写为:

(loop for x1 in x and y1 in y
      collect (+ x y))

这比递归代码更清晰,更短。