3

我在FSharp有一个 seq 序列。如果谓词为它返回 true,我想将一个 seq 加入前一个。

样本:

let items = seq [seq[2;3;4];seq[1;5;6;7;1;9];seq[2;3;5;7]]

我想将一个 seq 加入到前一个,如果 seq 从 1 开始,那么结果应该是在这种情况下:

seq [seq[2;3;4;1;5;6;7;1;9];seq[2;3;5;7]]

有什么不错的功能性方法吗?

我刚刚开始将我的长计算过程从 C# 转换为 F#,并且我对即使在几个小时的工作和我对 FSharp 的初学者水平的了解后我可以实现的性能改进印象深刻。

我从亚马逊买了一本名为“Beginning F#”的书。这真的很棒,但我现在主要应该使用 seqs、lists、maps、collections 并且这个主题没有像我需要的那样详细解释。有人会这么好心地建议我一个关于这些主题的好资源吗?

提前谢谢!

4

4 回答 4

3
let joinBy f input =
  let i = ref 0
  input 
  |> Seq.groupBy (fun x ->
    if not (f x) then incr i
    !i)
  |> Seq.map (snd >> Seq.concat)

joinBy (Seq.head >> ((=) 1)) items
于 2011-07-19T14:21:50.150 回答
2

正如在其他解决方案中看到的那样,这个问题几乎与你的最后一个问题相反。因此,为了更好地衡量,我在这里给出了我的答案的修改版本:

let concatWithPreviousWhen f s = seq {
    let buffer = ResizeArray()

    let flush() = seq { 
        if buffer.Count > 0 then 
            yield Seq.readonly (buffer.ToArray())
            buffer.Clear() }

    for subseq in s do
        if f subseq |> not then yield! flush()
        buffer.AddRange(subseq)

    yield! flush() }

你像这样使用它:

seq [seq[2;3;4];seq[1;5;6;7;1;9];seq[2;3;5;7]]
|> concatWithPreviousWhen (Seq.head>>(=)1)
于 2011-07-19T14:53:24.467 回答
2

与您的最后一个问题一样,没有库函数可以做到这一点。最直接的解决方案是使用IEnumerator. 但是,您可以编写一个更普遍有用的函数(然后也可以用于其他目的)。

module Seq =
  /// Iterates over elements of the input sequence and groups adjacent elements.
  /// A new group is started when the specified predicate holds about the element
  /// of the sequence (and at the beginning of the iteration).
  /// For example: 
  ///    Seq.groupWhen isOdd [3;3;2;4;1;2] = seq [[3]; [3; 2; 4]; [1; 2]]
  let groupWhen f (input:seq<_>) = seq {
    use en = input.GetEnumerator()
    let running = ref true

    // Generate a group starting with the current element. Stops generating
    // when it founds element such that 'f en.Current' is 'true'
    let rec group() = 
      [ yield en.Current
        if en.MoveNext() then
          if not (f en.Current) then yield! group() 
        else running := false ]

    if en.MoveNext() then
      // While there are still elements, start a new group
      while running.Value do
        yield group() }

为了解决最初的问题,您可以检查序列的第一个元素是否是 1 以外的数字。您将获得一个组序列,其中一个组是序列序列 - 然后您可以连接这些组:

items 
  |> Seq.groupWhen (fun s -> Seq.head s <> 1)
  |> Seq.map Seq.concat

编辑:我还在此处将函数作为片段发布(带有漂亮的 F# 格式):http: //fssnip.net/6A

于 2011-07-19T13:54:12.213 回答
1

对我来说看起来像一个折叠,如下所示。试图在没有参考值的情况下尽可能地发挥作用。

let joinBy f (s:'a seq seq) = 
    let (a:'a seq), (b:'a seq seq) = 
        s |> Seq.fold (fun (a,r) se -> 
                         if f se then (se |> Seq.append a,r) 
                         else (se, seq {yield! r; yield a} ) ) 
             (Seq.empty, Seq.empty)
    seq {yield! b; yield a} |> Seq.filter (Seq.isEmpty >> not)


seq [seq[2;3;4];seq[1;5;6;7;1;9];seq[2;3;5;7]]
|> joinBy (Seq.head >> ((=) 1))
|> printfn "%A"
于 2011-07-20T05:18:14.573 回答