F # array_chunk для последовательности - PullRequest
6 голосов
/ 04 апреля 2009

У меня возникли проблемы при создании последовательности. В основном мне нужно разделить последовательность на последовательность массивов. 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

Ответы [ 13 ]

0 голосов
/ 07 апреля 2009

Хорошая версия от Princess была исправлена, чтобы получить хвост и преобразована в 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 ] }
0 голосов
/ 07 апреля 2009

Мне нравится это решение лучше. Он генерирует новую последовательность из существующей последовательности (это означает, что для получения результата не нужно проходить через всю последовательность - это важно, если вы делаете что-то вроде обработки журналов, где вы не можете вызывать такие вещи, как Length). 1001 *

Я закончил тем, что написал сообщение в блоге с более подробной информацией о том, как я попал сюда.

module Seq =  

let grouped_by_with_leftover_processing f (опция f2: 'список -> список <' a>) (s: seq <'a>) = let rec grouped_by_with_acc (f: 'a ->' список -> 'опция списка *' список) acc (т.е. IEnumerator <'a>) = seq { if ie.MoveNext () затем let nextValue, leftovers = f ie.Current acc если nextValue.IsSome, то привести к nextValue.Value Уступать! grouped_by_with_acc для остатков т.е. еще пусть rems = f2 соотв если rems.Ssome, то дают rems.Value } seq { Уступать! grouped_by_with_acc f [] (s.GetEnumerator ()) }

let YieldReversedLeftovers (f: 'a list) = если f.IsEmpty тогда нет еще некоторые (List.rev f)

let grouped_by f s = grouped_by_with_leftover_processing f YieldReversedLeftovers s

let group_by_length_n n s = let grouping_function newValue acc = let newList = newValue :: acc // Если у нас правильная длина, возвращаем // Некоторые в качестве первого значения. Это будет // получим по последовательности. если List.length acc = n - 1 затем Some (List.rev newList), [] // Если у нас нет правильной длины, // использовать None (поэтому ничего не будет получено) еще нет, newList
grouped_by grouping_function s

Большие последовательности не являются проблемой:

seq {для i в 1..1000000000 -> i} |> Seq.group_by_length_n 3 ;; val it: seq = seq [[1; 2; 3]; [4; 5; 6]; [7; 8; 9]; [10; 11; 12]; ...] >
0 голосов
/ 06 апреля 2009

Вот еще один подход с некоторым сопоставлением с образцом - он больше похож на * .iter, и я получил его, выплевывая списки, а не массивы, поскольку именно так я обычно люблю свои данные.

let sequence_in_lists_of_length_n_with_acc (s: seq<'a>) n acc = seq {
use e = s.GetEnumerator()
let rec next_with_acc acc =
  match e.MoveNext(), acc with
  | true, a when List.length a + 1 = n ->  
    yield (List.rev (e.Current :: a))
    next_with_acc []
  | true, _ -> next_with_acc (e.Current :: acc)
  | false, _ ->
    f(List.rev acc)
  ()
next_with_acc []

}

...