如何遍历向量向量?

时间:2017-04-15 18:58:46

标签: loops vector common-lisp

我有这样的数据结构:

(defparameter *test*
  #(#(3)
    #(7 4)
    #(2 4 6)
    #(8 5 9 3)))

表示整数和函数的三角形

(defun get-node (i j triangle)
  "Returns the value of the node I J of TRIANGLE."
  (assert (<= j i) (i j) "~&J has to be smaller than I! I: ~D J: ~D~%" i j)
  (svref (svref triangle i) j))

允许我轻松访问三角形的每个节点(至少我认为是这样)。

我认为使用这两个很容易沿着从顶部(3)到底部的三角形的不同路径建立总和,例如8 - &gt; 3 + 7 + 2 + 8 = 20.下一条路径是3 + 7 + 2 + 5 = 17.

不幸的是,我完全无法以合理的方式生成向量矢量的索引,以便在这个简单的例子中找到所有八个路径。这不是关于暴力与动态编程等。我只是在寻找一种方法来遍历*test*的列和行,以确保找到每个路径。

如何迭代这两个指数,例如ij以这种方式找到了正确的组合?

3 个答案:

答案 0 :(得分:2)

我想我现在更好地理解你想要列举的内容,所以这里是 关于替代定义的问题的另一个答案 “路径”:路径可以描述为一系列方向, 向下(0)或向右(1)。这是简单的映射 无符号整数0≤path&lt; 2 path-directions 其中每个位代表连续的方向。每条路径都是 方便地由货币对表示(path-directionsnumber) 其中path-directions是方向的数量,number 编码位0到path-directions - 1的连续方向。

(defun gen-paths (path-directions)
  (loop for i below (expt 2 path-directions)
        collect i))

(gen-paths 3) =&gt; (0 1 2 3 4 5 6 7)

(defun path-to-directions (path-directions path)
  (loop for i downfrom (- path-directions 1) to 0
        collect (elt #(:down :down-right)
                     (ldb (byte 1 i) path))))

(loop for path in (gen-paths 3) collect (path-to-directions 3 path)) =&gt;

((:DOWN :DOWN :DOWN)             (:DOWN :DOWN :DOWN-RIGHT)
 (:DOWN :DOWN-RIGHT :DOWN)       (:DOWN :DOWN-RIGHT :DOWN-RIGHT)
 (:DOWN-RIGHT :DOWN :DOWN)       (:DOWN-RIGHT :DOWN :DOWN-RIGHT)
 (:DOWN-RIGHT :DOWN-RIGHT :DOWN) (:DOWN-RIGHT :DOWN-RIGHT :DOWN-RIGHT))

请注意,path-directions比其大小少一个 三角形。当您将路径表示为节点列表时 有一个额外的元素,起始节点(0,0)。

(defun path-to-ref (path-directions path)
  "Map a path to the list of (I J) pairs as understood by `get-node`."
  (loop for i upto path-directions
        for j = 0 then (+ j (ldb (byte 1 (- path-directions i)) path))
        collect (list i j)))

(loop with path-directions = (- (length *test*) 1)
      for path in (gen-paths path-directions)
      collect (path-to-ref path-directions path))

=&GT;

(((0 0) (1 0) (2 0) (3 0)) ((0 0) (1 0) (2 0) (3 1))
 ((0 0) (1 0) (2 1) (3 1)) ((0 0) (1 0) (2 1) (3 2))
 ((0 0) (1 1) (2 1) (3 1)) ((0 0) (1 1) (2 1) (3 2))
 ((0 0) (1 1) (2 2) (3 2)) ((0 0) (1 1) (2 2) (3 3))) 

(defun get-path-nodes (path triangle)
  "Returns the values of the nodes along PATH in TRIANGLE"
  (loop with path-directions = (- (length triangle) 1)
        for (i j) in (path-to-ref path-directions path)
        collect (get-node i j triangle)))

然后您可以轻松获取值:

(loop with path-directions = (- (length *test*) 1)
      for path in (gen-paths path-directions)
      collect (get-path-nodes path *test*))

=&GT;

((3 7 2 8) (3 7 2 5) (3 7 4 5) (3 7 4 9)
 (3 4 4 5) (3 4 4 9) (3 4 6 9) (3 4 6 3))

或总结他们

(loop with path-directions = (- (length *test*) 1)
      for path in (gen-paths path-directions)
      collect (loop for v in (get-path-nodes path *test*)
                    sum v))

=&GT;

(20 17 19 23 16 20 22 16)

答案 1 :(得分:2)

Lisp毫不奇怪地对列表提供了更友好的支持,所以我选择了:

 (defparameter *pyre*
  '((3)
    (7 4)
    (2 4 6)
    (8 5 9 3)))

我的第一次刺伤只是收集一棵树而且是重复的,但也许更容易理解:

 (defun summit (n levels)
   (destructuring-bind (this . more) levels
    (let ((me (nth n this)))
      (if more
          (list
           (list* me (summit n more))
           (list* me (summit (1+ n) more)))
        (list me)))))

通过评估测试:

 (summit 0 *pyre*)

现在收紧它并计算总和:

(defun summit (n levels)
  (destructuring-bind (this . more) levels
    (let ((me (nth n this)))
      (if more
          (loop for fork below 2
                nconc (summit (+ n fork) more) into fork-sums
                finally (return (loop for fs in fork-sums
                                      collecting (+ me fs))))
        (list me)))))

如果重新计算路径与您有关,我们可以获得一些乐趣,并在一个级别上使用现有汽车的总和以及在下一级计算的每个值。留下来作为练习。

附录:好的,rplaca不需要重复计算,令我非常失望(但道德是递归通过沿着数据中隐含的路径汇集数据而使得显式路径管理变得不必要):

(defun summit-ex (levels)
  (destructuring-bind (level . lower-levels) levels
    (if lower-levels 
      (loop with next-result = (let ((nr (summit-ex lower-levels)))
                                 (print nr)
                                 nr)
          for pos upfrom 0
          for value in level
          for next-values = (loop for fork below 2
                                  append (nth (+ pos fork) next-result))
          collect (loop for next-value in next-values
                        collecting (+ value next-value)))
      (mapcar 'list level))))

调试打印留在原地,因为我喜欢他们如何弄清楚发生了什么:

((8) (5) (9) (3)) 
((10 7) (9 13) (15 9)) 
((17 14 16 20) (13 17 19 13)) 
((20 17 19 23 16 20 22 16))

同时,是的,有些语言对矢量更友好,例如Clojure:

(def pyre [[3]
           [7 4]
           [2 4 6]
           [8 5 9 3]])

(defn summit-ex [[level & lower-levels]]
  (if lower-levels
    (let [next-result (summit-ex lower-levels)]
      (for [pos (range (count level))]
        (let [value (nth level pos)
              next-values (mapcat #(nth next-result (+ pos %))
                                  [0 1])]
          (map #(+ value %) next-values))))
    (map list level)))

我,我更喜欢Lisp。

答案 2 :(得分:0)

要生成“路径”,即您可以执行的(i j) s序列:

(defun gen-paths (depth)
  (if (plusp depth)
      (loop for p in (gen-paths (- depth 1))
            nconc (loop for j to depth
                        collect (append p (list (list depth j)))))
      '(((0 0)))))

例如(gen-paths 3) =&gt;

(((0 0) (1 0) (2 0) (3 0)) ((0 0) (1 0) (2 0) (3 1)) ((0 0) (1 0) (2 0) (3 2)) ((0 0) (1 0) (2 0) (3 3))
 ((0 0) (1 0) (2 1) (3 0)) ((0 0) (1 0) (2 1) (3 1)) ((0 0) (1 0) (2 1) (3 2)) ((0 0) (1 0) (2 1) (3 3))
 ((0 0) (1 0) (2 2) (3 0)) ((0 0) (1 0) (2 2) (3 1)) ((0 0) (1 0) (2 2) (3 2)) ((0 0) (1 0) (2 2) (3 3))
 ((0 0) (1 1) (2 0) (3 0)) ((0 0) (1 1) (2 0) (3 1)) ((0 0) (1 1) (2 0) (3 2)) ((0 0) (1 1) (2 0) (3 3))
 ((0 0) (1 1) (2 1) (3 0)) ((0 0) (1 1) (2 1) (3 1)) ((0 0) (1 1) (2 1) (3 2)) ((0 0) (1 1) (2 1) (3 3))
 ((0 0) (1 1) (2 2) (3 0)) ((0 0) (1 1) (2 2) (3 1)) ((0 0) (1 1) (2 2) (3 2)) ((0 0) (1 1) (2 2) (3 3)))

然后,您可以将get-node映射到此,以将这些索引转换为相应的值:

(loop for path in (gen-paths 3)
      collect (loop for (i j) in path
                    collect (get-node i j *test*)))

=&GT;

((3 7 2 8) (3 7 2 5) (3 7 2 9) (3 7 2 3)
 (3 7 4 8) (3 7 4 5) (3 7 4 9) (3 7 4 3)
 (3 7 6 8) (3 7 6 5) (3 7 6 9) (3 7 6 3)
 (3 4 2 8) (3 4 2 5) (3 4 2 9) (3 4 2 3)
 (3 4 4 8) (3 4 4 5) (3 4 4 9) (3 4 4 3)
 (3 4 6 8) (3 4 6 5) (3 4 6 9) (3 4 6 3))

或将每个“路径”的值相加

(loop for path in (gen-paths 3)
      collect (loop for (i j) in path
                    summing (get-node i j *test*)))

=&GT;

(20 17 21 15 22 19 23 17 24 21 25 19 17 14 18 12 19 16 20 14 21 18 22 16)