【问题标题】:In clojure how to lazilly calculate several sub sequences out of a big lazy sequence?在clojure中,如何从一个大的懒惰序列中懒惰地计算几个子序列?
【发布时间】:2014-06-15 13:33:48
【问题描述】:

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

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

(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 将永远不会返回!

【问题讨论】:

  • positions 是否像您的代码片段中一样是固定列表?如果是这样,您可以在 subvec 调用中使用 (vec (take n big-sequence)),在这种情况下 n = 47。
  • @RandyHudson 它将解决无限序列的问题,但不会保持懒惰。
  • map 本身就是懒惰的,所以如果你只想要一些结果,那么大序列只能实现到需要的位置。此外,takeing 某些元素不会强制实现整个序列。
  • 子向量总是排序的吗?它们在示例中。正如@omiel 指出的那样,我在我的解决方案中已经假设了。

标签: clojure lazy-evaluation


【解决方案1】:

您要求的是惰性序列的子向量的惰性序列。我们可以按如下方式逐层开发。

(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])

【讨论】:

  • 不错!注意:这假设位置已排序,并且(尽管在提供的示例中就是这种情况)我在问题中看不到任何暗示。
  • @omiel 由OP's solution判断,位置不需要排序,此方案无效。
  • 那太糟糕了。如果位置未排序,则必须保留对序列头部的引用。对于一个大(可能是无限的)序列和大位置,它是一个潜在的 OutOfMemoryError 盯着你的脸。
  • @omiel 太真实了。 OP's answermy other attempt 也没有帮助:即使大序列是惰性的,随着更大位置的出现,越来越多的序列会被实现(或者,在我的情况下,移动到向量)。
【解决方案2】:

这以比@alfredx's solution 甚至as improved by OP 更快的形式运行@schauho's suggestion。与my previous solution 不同,它不假设所需的子向量已排序。

基本工具是split-at的热切模拟:

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

这会从tail 中删除第一个n 项目,将它们附加到向量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 在很短的恒定时间内工作,因此它需要线性时间 消耗的大序列的数量。
  • my previous solution 不同,它不会忘记自己的头部:它会保持 内存中所有观察到的大序列。

【讨论】:

    【解决方案3】:
    (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】:

      您可以在具有最大位置的大序列上使用take。无论如何,您都需要计算到目前为止的值来计算子向量,因此您不会真正“丢失”任何东西。

      【讨论】:

      • 我失去了一些东西:我必须预先意识到大的惰性序列,而不仅仅是在我需要的时候。
      【解决方案5】:

      诀窍是使用takedrop 编写subvec 的惰性版本:

      (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))
      

      【讨论】:

      • 您最好在sub-sequence 的末尾加上vec,因为您的问题需要子向量
      猜你喜欢
      • 1970-01-01
      • 2012-04-09
      • 2014-05-31
      • 2012-12-29
      • 1970-01-01
      • 1970-01-01
      • 2016-10-07
      • 2010-12-17
      • 1970-01-01
      相关资源
      最近更新 更多