4

我正在学习 clojure(来自 ruby​​)并且在思考生成集合的最佳方法时遇到了一些麻烦。

我想编写一个函数,它接受两个参数——向量ary和整数sum——并生成一个新的二维向量,其中每一行的总和是 <= 输入总和(忽略输入验证)。我遇到问题的概念点是如何在建立新集合的同时保持“当前行的总和”的状态。

这是我所拥有的:

(defn split-after-sum [ary sum]
  (reduce (fn [acc i]
            (let [inner-sum (+ (last acc) i)]
              (if (< inner-sum sum)
                [(conj (first acc) i) (+ i (last acc))]
                [(conj (first acc) i "X") 0])))
          [[] 0] ary))

我正在传递reduce一个 2 元素向量,以便跟踪我正在构建的集合和该行的总数。

它有点工作。我还没有弄清楚如何实际使结果成为二维数组,所以它只是将“X”粘贴在应该拆分的位置:

(first (split-after-sum [1 1 1 1 1 1 1 1 1] 2)) => [1 1 "X" 1 1 "X" 1 1 "X" 1 1 "X" 1]

理想的输出是:

(split-after-sum [1 1 1 1 1 1 1 1 1] 2) => [[1 1] [1 1] [1 1] [1 1] [1]]

我知道这里有一些混乱的东西,但我认为对这个问题的惯用答案会很有启发性。

4

2 回答 2

7
(defn split-after-sum [ary sum]
  (let [[acc v] (reduce (fn [[acc v s] x]
                          (let [new-s (+ s x)]
                            (if (<= new-s sum)
                              [acc (conj v x) new-s]
                              [(conj acc v) [x] x])))
                        [[] [] 0]
                        ary)]
    (conj acc v)))

(split-after-sum [1 1 3 2 1 1 1 1 1] 3)
;= [[1 1] [3] [2 1] [1 1 1] [1]]
(split-after-sum [1 1 3 2 1 1 1 1 1] 4)
;= [[1 1] [3] [2 1 1] [1 1 1]]
(split-after-sum [1 1 3 2 1 1 1 1 1] 5)
;= [[1 1 3] [2 1 1 1] [1 1]]
(split-after-sum [1 1 3 2 1 1 1 1 1] 6)
;= [[1 1 3] [2 1 1 1 1] [1]]
于 2013-08-03T21:18:36.777 回答
2

正如 Michał 所示,累积状态 a 可以与相关元素连接到一个元组中。

下面是一种命令式方法,其中状态保存在循环绑定中。

(defn split-after-sum [ary sum]
  (when (seq ary)
    (loop [ary ary
           cur 0
           row []
           rows []]
      (if-let [[x & xs] ary]
        (let [nxt (+ x cur)]
          (if (<= nxt sum)
            (recur xs nxt (conj row x) rows)
            (recur xs x [x] (conj rows row))))
        (conj rows row)))))

另外:参数顺序可能应该颠倒。处理序列元素的函数倾向​​于将序列作为最后一个参数。

于 2013-08-03T21:33:33.670 回答