在clojure中,我想从一个大的懒惰序列中计算几个子向量(可能是无限的)。 天真的方法是将惰性序列转换为向量,然后计算子向量。但是在这样做的时候,我正在失去懒惰。
我有一个大序列big-sequence
和positions
,一个开始和结束位置列表。我想做以下计算,但 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
永远不会返回!
答案 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
,并将新的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
在短暂的恒定时间内工作,因此需要线性时间
消耗的大量序列的数量。答案 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)
诀窍是使用subvec
和take
编写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))