我在制作序列时遇到了一些麻烦。基本上我需要将序列切割成一系列数组。 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
答案 0 :(得分:5)
这是一个很好的命令,可以使用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] }
答案 1 :(得分:5)
我爱Seq.take
&amp; 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 )
答案 2 :(得分:4)
这个答案可能会被埋没,但这是我对这个问题的看法:
let chunk n xs =
xs
|> Seq.mapi(fun i x -> i/n, x)
|> Seq.groupBy fst
|> Seq.map (fun (_, g) -> Seq.map snd g)
优点:
缺点:
答案 3 :(得分: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个元素)。此外,这不如显式使用枚举器那样有效,但它更优雅。
答案 4 :(得分: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)
}
(代码取自我的回答here)
答案 5 :(得分: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
答案 6 :(得分:0)
这是另一种模式匹配的方法 - 它看起来更像* .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 []
}
答案 7 :(得分:0)
我更喜欢这个解决方案。它从现有序列生成一个新序列(意味着它不需要遍历整个序列来获得结果 - 如果你正在执行类似日志处理的事情,那么这是很关键的,你不能调用像Length这样的东西。) / p>
我最后写了一篇blog post,详细介绍了我是如何到达这里的。
module Seq =
let grouped_by_with_leftover_processing f(f2:'a list - &gt; list&lt;'a&gt;选项)(s:seq&lt;'a&gt;)= let rec grouped_by_with_acc(f:'a - &gt;'列表 - &gt;'列表选项*'列表)acc(即:IEnumerator&lt;'a&gt;)= seq { if ie.MoveNext() 然后 let nextValue,leftovers = f ie.Current acc 如果nextValue.IsSome然后产生nextValue.Value 让! groups_by_with_acc f剩菜即 其他 让rems = f2 acc 如果rems.IsSome然后产生rems.Value } seq { 让! grouped_by_with_acc f [](s.GetEnumerator()) }
让YieldReversedLeftovers(f:'列表)= 如果f.IsEmpty 没有 其他一些(List.rev f)
let grouped_by f s = grouped_by_with_leftover_processing f YieldReversedLeftovers s
让group_by_length_n n s =
让grouping_function newValue acc =
让newList = newValue :: acc
//如果我们有合适的长度,请返回
//一些作为第一个值。那会
//由序列产生。
如果List.length acc = n - 1
一些(List.rev newList),[]
//如果我们没有合适的长度,
//使用无(因此不会产生任何结果)
否则,新列表
groups_by grouping_function s
大序列不是问题:
seq {for i in 1..1000000000 - &gt; i} |&gt; Seq.group_by_length_n 3 ;; val it:seq&lt; int list&gt; = seq [[1; 2; 3]; [4; 5; 6]; [7; 8; 9]; [10; 11; 12]; ...] &GT;答案 8 :(得分:0)
来自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 ] }
答案 9 :(得分: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的回答完全相同。
我不记得(链接?)一个序列不记得位置,所以连续的take / skip不是最佳的。
答案 10 :(得分:0)
这是@kvb的解决方案,修复了Seq.skip / take限制。它小巧,优雅,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)
}
答案 11 :(得分:0)
这是我的版本将数组作为输入和输出:
let chunk chunkNumber (array : _ array) =
let chunkSize = array.Length/chunkNumber
let mutable startIndex = 0
[|
let n1 = array.Length % chunkNumber
for i = 1 to n1 do
yield Array.sub array startIndex (chunkSize+1)
startIndex <- startIndex + chunkSize+1
let n2 = chunkNumber - n1
for i = 1 to n2 do
yield Array.sub array startIndex chunkSize
startIndex <- startIndex + chunkSize
|]
该函数尝试制作相似大小的块(而不是获得一个非常小的最后一个块)并以与构建序列相同的方式构建输出(使得重写它以便将序列作为输出变得容易)
答案 12 :(得分:0)
总结以上对序列,列表或数组的分块,缓冲或分段。两种形式:
let rec chunk size xs = seq {
yield Seq.take size xs
yield! chunk size (Seq.skip size xs)
}
或
let chunk size = Seq.unfold (fun xs ->
match Seq.isEmpty xs with
| false -> Some(Seq.take size xs, Seq.skip size xs)
| _ -> None
)
注意:如果Seq可以像游标一样正常工作(就像我期望的那样是一个懒惰的求值),则Seq.take将使Seq.skip的位置提前。但是,事实并非如此。