F#如何根据谓词而不是固定长度对序列进行窗口化

Chi*_*ble 5 f#

鉴于以下输入序列,我想生成所需的输出。我知道如果所有窗口都是固定长度,则 Seq.window 几乎可以用来获得所需的结果。但是,在这种情况下,它们不是固定长度,每当遇到“a”时,我想开始一个新序列。标准收藏库可以做到这一点吗?

let inputSequence = 
      ["a"; "b"; "c";
       "a"; "b"; "c"; "d";
       "a"; "b"; 
       "a"; "d"; "f";
       "a"; "x"; "y"; "z"]

let desiredResult = 
   [["a"; "b"; "c";]
    ["a"; "b"; "c"; "d";]
    ["a"; "b"; ]
    ["a"; "d"; "f";]
    ["a"; "x"; "y"; "z"]]
Run Code Online (Sandbox Code Playgroud)

The*_*Fox 6

这是一种使用可变状态但非常简洁的方法:

let mutable i = 0
[ for x in inputSequence do
    if x = "a" then i <- i + 1
    yield i, x ]
|> List.groupBy fst
|> List.map snd
|> List.map (List.map snd)
Run Code Online (Sandbox Code Playgroud)


Tom*_*cek 5

正如另一个答案中提到的,您可以使用递归或使用折叠相当轻松地实现此目的。为了使递归版本更有用,您可以定义一个函数chunkAt,当列表包含特定值时创建一个新块:

let chunkAt start list = 
  let rec loop chunk chunks list = 
    match list with
    | [] -> List.rev ((List.rev chunk)::chunks)
    | x::xs when x = start && List.isEmpty chunk -> loop [x] chunks xs
    | x::xs when x = start -> loop [x] ((List.rev chunk)::chunks) xs
    | x::xs -> loop (x::chunk) chunks xs
  loop [] [] list
Run Code Online (Sandbox Code Playgroud)

然后您可以使用以下命令在输入序列上运行它:

chunkAt "a" inputSequence
Run Code Online (Sandbox Code Playgroud)

尽管没有标准库函数可以执行此操作,但您可以使用数据序列操作库 Deedle,它实现了一组相当丰富的分块函数。要使用 Deedle 执行此操作,您可以将序列转换为按序数索引索引的序列,然后使用:

let s = Series.ofValues inputSequence
let chunked = s |> Series.chunkWhile (fun _ k2 -> s.[k2] <> "a")
Run Code Online (Sandbox Code Playgroud)

如果你想将数据转回列表,你可以使用Values返回的系列的属性:

chunked.Values |> Seq.map (fun s -> s.Values)
Run Code Online (Sandbox Code Playgroud)