3

FSharpに一連のseqがあります。述語がtrueに戻ったら、seqを前のseqに結合したいと思います。

サンプル:

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

seqが1で始まる場合、seqを前のseqに結合したいので、この場合の結果は次のようになります。

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

それを行うための素晴らしい機能的な方法はありますか?

長い計算プロセスをC#からF#に変換し始めたばかりで、ほんの数時間の作業とFSharpの初心者レベルの知識の後でさえ達成できるパフォーマンスの向上に非常に感銘を受けました。

アマゾンから「BeginningF#」という本を購入しました。それは本当に素晴らしいですが、私は主にseqs、lists、maps、collectionsで作業する必要があり、このトピックは必要なほど詳細に説明されていません。誰かが私にこのトピックについての良いリソースをアドバイスしてくれませんか?

事前にThx!

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

最後の質問と同様に、これを正確に実行するライブラリ関数はありません。最も簡単な解決策は、を使用してこれを強制的に記述すること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 に答える
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 に答える
1

以下に示すように、私には折り目のように見えます。ref値なしで可能な限り機能するように試みました。

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 に答える