6

シーケンスの作成に問題があります。基本的に、シーケンスを配列のシーケンスに切り刻む必要があります。Seq.windowedはほとんどそれを行いますが、重複する要素は必要ありません。

最初にすべてを配列に読み込むことで必要なものを取得できますが、シーケンスを使用したいと思います。

let array_chunk s (a:int[]) =
    Array.init (a.Length / s) (fun i -> Array.sub a (i * s) s)

someSequence |> Seq.to_array |> array_chunk 5
4

13 に答える 13

4

これは、seq で動作し、任意のサイズの配列を生成する、優れた命令型のものです。シーケンスが n で偶数でない場合、最後のものは小さくなります。

let chunk n xs = seq {
    let i = ref 0
    let arr = ref <| Array.create n (Unchecked.defaultof<'a>)
    for x in xs do
        if !i = n then 
            yield !arr
            arr := Array.create n (Unchecked.defaultof<'a>)
            i := 0 
        (!arr).[!i] <- x
        i := !i + 1
    if !i <> 0 then
        yield (!arr).[0..!i-1] }
于 2009-04-04T05:45:47.927 に答える
4

Seq.take私は&Seq.skipソリューションが大好きです。それは美しく、シンプルで非常に読みやすいですが、私は次のようなものを使用します:

let chunks n (sequence: seq<_>) =
    let fold_fce (i, s) value = 
        if i < n then (i+1, Seq.append s (Seq.singleton value))
                 else (  1, Seq.singleton value)
    in sequence
    |> Seq.scan (fold_fce) (0, Seq.empty)
    |> Seq.filter (fun (i,_) -> i = n)
    |> Seq.map (Seq.to_array << snd )

これは命令型のコードではなく、Seq.skip を使用するソリューションよりも効率的です。一方、入力シーケンスを n で割り切れる長さにトリミングします。この動作が受け入れられない場合は、簡単な変更で修正できます。

let chunks n (sequence: seq<_>) =
    let fold_fce (i, s) value = 
        if i < n then (i+1, Seq.append s (Seq.singleton value))
                 else (  1, Seq.singleton value)
    in sequence
    |> Seq.map (Some)
    |> fun s -> Seq.init_finite (n-1) (fun _ -> None) |> Seq.append s
    |> Seq.scan (fold_fce) (0, Seq.empty)
    |> Seq.filter (fun (i,_) -> i = n) 
    |> Seq.map (Seq.to_array << (Seq.choose (id)) << snd )
于 2009-04-04T17:47:45.150 に答える
3

どうですか:

let rec chunks n sq =
  if not (Seq.is_empty sq) then 
    seq {
      yield Seq.take n sq |> Seq.to_array
      yield! chunks n (Seq.skip n sq)
    }
  else
    Seq.empty

これには、sqにnで割り切れる数の要素が必要であることに注意してください(Seq.takeおよびSeq.skipは、LINQのTakeおよびSkip拡張メソッドとは異なり、シーケンスに少なくともn個の要素が含まれている必要があるため)。また、これは列挙子を明示的に使用するほど効率的ではありませんが、よりエレガントです。

于 2009-04-04T04:47:23.030 に答える
3

拡張機能として、テイク/スキップ回答の修正版。不均一な長さで機能するはずです。動作保証はありませんが…

 module Seq = 
    let rec chunks n (s:#seq<_>) =
        seq {
                 if Seq.length s <= n then
                    yield s
                 else
                    yield Seq.take n s
                    yield! chunks n (Seq.skip n s)           
            }

(ここの私の答えから取られたコード)

于 2009-04-07T08:14:53.753 に答える
1

これは素晴らしく簡潔です。

let chunk size (arr : 'a array) =
    [| for a in 0 .. size .. arr.Length - size -> arr.[a..a + size - 1] |]

ただし、これは配列内の最後の (arr.Length % size) 要素を削除します。不足している要素を取得して Array.append を使用することで、これを修正できます。

let chunk2 size (arr : 'a array) = 
    let first = [| for a in 0 .. size .. arr.Length - size -> arr.[a..a + size - 1] |]
    let numberOfMissingElements = arr.Length - (first.Length * size)
    if numberOfMissingElements > 0 then
        let last = [| arr.[arr.Length - numberOfMissingElements..] |]
        Array.append first last
    else first
于 2009-04-04T23:27:15.160 に答える
0

これはどう :

let grouped n = 
   Seq.unfold(fun s -> if not (Seq.isEmpty s) then 
                           Some (Seq.take n s, Seq.skip n s) 
                       else None) 

それはkvbの答えと同じ静脈にあります。

シーケンスが位置を覚えていないので、連続したテイク/スキップが最適ではないことをどういうわけか覚えています(リンク?)。

于 2012-11-21T17:18:44.693 に答える
0

私はこのソリューションの方が好きです。既存のシーケンスから新しいシーケンスを生成します (つまり、結果を取得するためにシーケンス全体をトラバースする必要はありません。これは、Length などを呼び出すことができないログ処理などを行う場合に重要です)。

どうやってここにたどり着いたかについて、より詳細なブログ投稿を書くことになりました。

module Seq =  

let grouped_by_with_leftover_processing f (f2: 'a list -> list<'a> オプション) (s: seq<'a>)= let rec grouped_by_with_acc (f: 'a -> 'a list -> 'a list オプション * 'a list) acc (ie: IEnumerator<'a>) = seq { if ie.MoveNext() then let nextValue, leftovers = f ie.Current acc if nextValue.IsSome then yield nextValue.Value yield! grouped_by_with_acc f 残り物 ie else let rems = f2 acc if rems.IsSome then yield rems.Value } seq { yield! grouped_by_with_acc f [] (s.GetEnumerator()) }

let YieldReversedLeftovers (f: 'a list) = if f.IsEmpty then None else Some (List.rev f)

let grouped_by fs = grouped_by_with_leftover_processing f YieldReversedLeftovers s

let group_by_length_n ns = let grouping_function newValue acc = let newList = newValue :: acc // 正しい長さの場合、最初の値として Some を返します。それは // シーケンスによって生成されます。if List.length acc = n - 1 then Some (List.rev newList), [] // 適切な長さがない場合は // None を使用 (何も生成されない) else None, newList
grouped_by grouping_function s

大きなシーケンスは問題ではありません:

seq { for i in 1..1000000000 -> i} |> Seq.group_by_length_n 3;; val it : seq<int list> = seq [[1; 2; 3]; [4; 5; 6]; [7; 8; 9]; [10; 11; 12]; ...] >
于 2009-04-06T20:12:00.450 に答える
0

これは、Seq.skip/take の制限が修正された @kvb のソリューションです。小さく、エレガントで、O(n) です。

let eSkip n s = System.Linq.Enumerable.Skip(s, n)

let rec seq_chunks n sq =
  if (Seq.isEmpty sq) 
  then Seq.empty
  else seq {
      yield Seq.truncate n sq
      yield! seq_chunks n (eSkip n sq)
 }
于 2016-03-21T19:49:27.303 に答える
0

Princess の Nice バージョンは、tail を取得するように修正され、seq に変換されました

let array_chunk size (arr : 'a array) = 
    let maxl = arr.Length - 1
    seq { for a in 0 .. size .. maxl -> arr.[a .. min (a + size - 1) maxl ] }
于 2009-04-07T05:25:03.910 に答える