F#以块为单位划分序列

时间:2013-12-01 06:31:48

标签: f# ienumerable grouping

我有一些Seq<'T>我需要一个功能:

Seq.block : int -> Seq<'T> -> Seq<Seq<'T>>

其中结果序列是原始序列,以固定长度的块分割。

所以,例如Seq.block 2 {1; 2; 3; 4; 5}必须返回{{1; 2}; {3; 4}; {5}}

问题在于,它必须同时适用于有限和无限序列。我有各种各样的尝试,最后一个是工作,但我希望这可以做得更清洁。我已经离开了我在这里的各种非工作尝试,希望这对某人也有帮助。

我的第一次尝试是:

let block size = Seq.mapi (fun i x -> (i/size,x))
                 >> Seq.groupBy fst
                 >> Seq.map (fun (_,res) -> Seq.map snd res)

这适用于有限序列,但不适用于无限序列(groupBy不起作用,回想起来很明显)。

作为我试过的第二次尝试(希望折叠会懒散地运作)

let block size =  Seq.mapi (fun i x -> (i/size,x))
                  >> Seq.fold (fun (accIx,fullSeq,buildSeq) (ix,el) ->
                                        if accIx = ix then (accIx,fullSeq, Seq.append buildSeq (Seq.singleton el))
                                        else (accIx+1,Seq.append fullSeq (seq {yield buildSeq}),Seq.singleton el))
                                  (0, Seq.empty,Seq.empty)
                  >> fun (_,full,rem) -> Seq.append full (seq { yield rem})

但这甚至更糟糕(在第一次尝试有效的测试示例中引发了stackoverflow,尽管我的版本可能已经失去了改进的空间)。

对于尝试#3,我做了:

let block size x = Seq.initInfinite (fun i -> x |> Seq.skip (i*size)
                                                |> Seq.take size)

这实际上适用于无限序列,但是a。它不适用于有限序列和b。当你在序列的尾部进一步走出时,可能会遇到性能问题。我已经知道的递归尝试#4注定要失败,但我想我还是试一试:

let rec block size x = 
    Seq.append (seq { yield Seq.take size x})
               (block size (Seq.skip size x))

尝试#5几乎有效:

let block size = Seq.windowed size
                 >> Seq.mapi (fun i x -> (i,x))
                 >> Seq.filter (fun (i,_) -> i % size = 0)
                 >> Seq.map (fun (_,x) -> Array.toSeq x)

但有(轻微)缺点,对于有限序列,当序列长度不能被'size'整除时,最后一个短元素不在列表中。接下来,由于生成了所有数组,我对性能略感担忧。

稍微考虑一下,我想出了尝试#6:

let block size = Seq.mapi (fun i x -> (i % size,x))
                 >> Seq.unfold (fun state -> if Seq.isEmpty state then None
                                             else
                                                 match Seq.tryFind (fun (i,_) -> i = size-1) state with
                                                        | None  -> state |> Seq.map snd,
                                                                   Seq.empty
                                                        | _     -> state |> Seq.take size
                                                                         |> Seq.map snd,
                                                                   Seq.skip size state
                                                    |> Some)

很高兴听到是否有更好的选择。

0 个答案:

没有答案