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

在clojure中,如何从一个大的惰性序列中惰性地计算出几个子序列?,clojure,lazy-evaluation,Clojure,Lazy Evaluation,在clojure中,我想从一个大的惰性序列(可能是无限序列)中计算几个子向量。 最简单的方法是将惰性序列转换为向量,然后计算子向量。但是当我这么做的时候,我就失去了懒惰 我有一个大序列大序列和位置,一个开始和结束位置的列表。我想做以下计算,但懒洋洋地计算: (let [positions '((5 7) (8 12) (18 27) (28 37) (44 47)) big-sequence-in-vec (vec big-sequence)] (map #(subvec b

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

我有一个大序列
大序列
位置
,一个开始和结束位置的列表。我想做以下计算,但懒洋洋地计算:

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

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

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

(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])
这是一种比,甚至更快的形式。与此不同,它不假定所需的子向量已排序

基本工具是对以下各项的一种模拟:

这将从
tail
中删除第一个
n
项,将它们附加到向量
v
,并将新的
v
tail
作为向量返回。我们使用它来捕获向量中的大序列,正如提供每个子向量所需的那样

(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
    在较短的恒定时间内工作,因此它在 消耗的大序列的数量
  • 与之不同的是,它不会忘记它的头:它保持 在内存中观察到的所有大序列

诀窍是使用
take
drop
编写一个延迟版本的
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))

positions
是否是代码片段中的固定列表?如果是这样的话,您可以在subvec调用中使用
(vec(取n个大序列))
,在这种情况下,n=47。@RandyHudson它将解决无限序列的问题,但它不会保持惰性。
map
本身就是惰性的,因此如果您只想要一些结果,大序列只能实现到需要的位置。另外,
take
ing某些元素不会强制实现整个序列。子向量是否总是排序?它们在示例中。正如@omiel所指出的,在我的解决方案中,我已经假设了这一点。我正在失去一些东西:我必须提前意识到大的懒惰序列,而不仅仅是在我需要它的时候。太好了!注意:这假设位置已排序,并且(尽管在提供的示例中是这种情况),我在问题中看不到任何暗示。@omiel根据判断,位置不需要排序,此解决方案无效。这太糟糕了。如果未对位置进行排序,则必须保留对序列头的引用。有一个大的(可能是无限的)序列和大的位置,这是一个潜在的OutOfMemoryError盯着你的脸看。@omiel太正确了。也没有,也没有帮助:即使大序列是惰性的,随着更大位置的出现,它越来越多地被实现(或者,在我的例子中,移动到向量)。你最好在
子序列
的末尾放一个
向量
,因为你的问题需要子向量。
(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])
(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))