在clojure中,如何从一个大的懒惰序列中计算出几个子序列?

时间:2014-06-15 13:33:48

标签: clojure lazy-evaluation

在clojure中,我想从一个大的懒惰序列中计算几个子向量(可能是无限的)。 天真的方法是将惰性序列转换为向量,然后计算子向量。但是在这样做的时候,我正在失去懒惰。

我有一个大序列big-sequencepositions,一个开始和结束位置列表。我想做以下计算,但 lazilly

(let [positions '((5 7) (8 12) (18 27) (28 37) (44 47))
      big-sequence-in-vec (vec big-sequence)]
    (map #(subvec big-sequence-in-vec (first %) (second %)) positions))
; ([5 6] [8 9 10 11] [18 19 20 21 22 23 24 25 26] [28 29 30 31 32 33 34 35 36] [44 45 46])

可行吗?

备注:如果big-sequence为无限,则vec永远不会返回!

5 个答案:

答案 0 :(得分:3)

您要求延迟序列的懒惰序列的子载体。我们可以如下逐层开发它。

(defn sub-vectors [spans c]
  (let [starts (map first spans)                 ; the start sequence of the spans
        finishes (map second spans)              ; the finish sequence of the spans

        drops (map - starts (cons 0 starts))                    ; the incremental numbers to drop
        takes (map - finishes starts)                           ; the numbers to take

        tails (next (reductions (fn [s n] (drop n s)) c drops)) ; the sub-sequences from which the sub-vectors will be taken from the front of
        slices (map (comp vec take) takes tails)]               ; the sub-vectors
    slices))

例如,给定

(def positions '((5 7) (8 12) (18 27) (28 37) (44 47)))

我们有

(sub-vectors positions (range))
; ([5 6] [8 9 10 11] [18 19 20 21 22 23 24 25 26] [28 29 30 31 32 33 34 35 36] [44 45 46])

跨度和基本序列都被懒惰地处理。两者都可以是无限的。

例如,

(take 10 (sub-vectors (partition 2 (range)) (range)))
; ([0] [2] [4] [6] [8] [10] [12] [14] [16] [18])

答案 1 :(得分:1)

这会以比@schauho's suggestion更快的形式@alfredx's solution得出as improved by OP,甚至my previous solution。与split-at不同,它不假设对所需的子向量进行排序。

基本工具是my previous solution

的急切模拟
(defn splitv-at [n v tail]
  (if (and (pos? n) (seq tail))
    (recur (dec n) (conj v (first tail)) (rest tail))
    [v tail]))

这会移除n中的第一个tail项,将其附加到向量v,并将新的vtail作为向量返回。我们使用它来捕获向量中的更多大序列,以便在它出现时提供每个子向量。

(defn sub-spans [spans coll]
  (letfn [(sss [spans [v tail]]
               (lazy-seq
                (when-let [[[from to] & spans-] (seq spans)]
                  (let [[v- tail- :as pair] (splitv-at (- to (count v)) v tail)]
                    (cons (subvec v- from to) (sss spans- pair))))))]
    (sss spans [[] coll])))

例如

(def positions '((8 12) (5 7) (18 27) (28 37) (44 47)))

(sub-spans positions (range))
; ([8 9 10 11] [5 6] [18 19 20 21 22 23 24 25 26] [28 29 30 31 32 33 34 35 36] [44 45 46])
  • 由于subvec在短暂的恒定时间内工作,因此需要线性时间 消耗的大量序列的数量。
  • 与{{3}}不同,它不会忘记它的头:它保持不变 记忆中所有观察到的大序列。

答案 2 :(得分:0)

您可以使用最大位置的大序列使用take。无论如何,你需要计算到目前为止的值来计算子向量,所以你真的不会失去"任何东西。

答案 3 :(得分:0)

(defn pos-pair-to-vec [[start end] big-sequence] 
  (vec (for [idx (range start end)]
            (nth big-sequence idx))))

(let [positions '((5 7) (8 12) (18 27) (28 37) (44 47))
      big-seq (range)]
  (map #(pos-pair-to-vec % big-seq) positions))

答案 4 :(得分:0)

诀窍是使用subvectake编写drop的惰性版本:

(defn subsequence [coll start end]
  (->> (drop start coll)
       (take (- end start))))

(let [positions '((5 7) (8 12) (18 27) (28 37) (44 47))
      big-sequence (range)]
  (map (fn [[start end]]  (subsequence big-sequence start end)) positions))
;((5 6) (8 9 10 11) (18 19 20 21 22 23 24 25 26) (28 29 30 31 32 33 34 35 36) (44 45 46))