说我有100个元素的序列。每10个元素我想要一个前10个元素的新列表。在这种情况下,我将得到10个子列表的列表。
Seq.take(10)看起来很有希望,我怎样才能反复调用它来返回列表清单?
答案 0 :(得分:14)
现在有Seq.chunkBySize
可用:
[1;2;3;4;5] |> Seq.chunkBySize 2 = seq [[|1; 2|]; [|3; 4|]; [|5|]]
答案 1 :(得分:7)
这还不错:
let splitEach n s =
seq {
let r = ResizeArray<_>()
for x in s do
r.Add(x)
if r.Count = n then
yield r.ToArray()
r.Clear()
if r.Count <> 0 then
yield r.ToArray()
}
let s = splitEach 5 [1..17]
for a in s do
printfn "%A" a
(*
[|1; 2; 3; 4; 5|]
[|6; 7; 8; 9; 10|]
[|11; 12; 13; 14; 15|]
[|16; 17|]
*)
答案 2 :(得分:2)
我有三种解决方案的演变。它们都没有保留输入元素的顺序,希望没问题。
我的第一个解决方案非常难看(使用ref cell):
//[[4; 3; 2; 1; 0]; [9; 8; 7; 6; 5]; [14; 13; 12; 11; 10]; [17; 16; 15]]
let solution1 =
let split s n =
let i = ref 0
let lst = ref []
seq {
for item in s do
if !i = n then
yield !lst
lst := [item]
i := 1
else
lst := item::(!lst)
i := !i+1
yield !lst
} |> Seq.toList
split {0..17} 5
我的第二个解决方案在第一个解决方案中使用了ref cell,但因此强制使用直接IEnumerator访问(一边推,另一边弹出)!
//[[17; 16; 15]; [14; 13; 12; 11; 10]; [9; 8; 7; 6; 5]; [4; 3; 2; 1; 0]]
let solution2 =
let split (s:seq<_>) n =
let e = s.GetEnumerator()
let rec each lstlst lst i =
if e.MoveNext() |> not then
lst::lstlst
elif i = n then
each (lst::lstlst) [e.Current] 1
else
each lstlst ((e.Current)::lst) (i+1)
each [] [] 0
split {0..17} 5
我的第三个解决方案基于第二个解决方案,除了它通过将列表作为输入而不是seq来“欺骗”,这使得使用模式匹配的最优雅解决方案,因为Tomas指出缺少seq(这就是为什么我们被迫使用直接IEnumerator访问)。
//[[17; 16; 15]; [14; 13; 12; 11; 10]; [9; 8; 7; 6; 5]; [4; 3; 2; 1; 0]]
let solution3 =
let split inputList n =
let rec each inputList lstlst lst i =
match inputList with
| [] -> (lst::lstlst)
| cur::inputList ->
if i = n then
each inputList (lst::lstlst) [cur] 1
else
each inputList lstlst (cur::lst) (i+1)
each inputList [] [] 0
split [0..17] 5
如果保留元素的顺序很重要,可以使用List.rev来实现此目的。例如,在solution2中,将split
函数的最后一行更改为:
each [] [] 0 |> List.rev |> List.map List.rev
答案 3 :(得分:0)
脱离我的头脑:
let rec split size list =
if List.length list < size then
[list]
else
(list |> Seq.take size |> Seq.toList) :: (list |> Seq.skip size |> Seq.toList |> split size)
答案 4 :(得分:0)
我认为Brian的解决方案可能是最合理的简单选择。带序列的问题是它们不能用通常的模式匹配(如功能列表)轻松处理。避免这种情况的一个选择是使用F#PowerPack中的LazyList
。
另一种选择是定义用于处理IEnumerator
类型的计算构建器。我最近写了类似的东西 - you can get it here。然后你可以写下这样的东西:
let splitEach chunkSize (s:seq<_>) =
Enumerator.toSeq (fun () ->
let en = s.GetEnumerator()
let rec loop n acc = iter {
let! item = en
match item with
| Some(item) when n = 1 ->
yield item::acc |> List.rev
yield! loop chunkSize []
| Some(item) ->
yield! loop (n - 1) (item::acc)
| None -> yield acc |> List.rev }
loop chunkSize [] )
这使得能够使用一些函数模式进行列表处理 - 最值得注意的是,您可以将其写为通常的递归函数(类似于您为列表/惰性列表编写的函数),但它必须在封面下({ } let!
的constructo接受下一个元素并修改枚举器。
答案 5 :(得分:0)
也许这个简单的纯实现可能很有用:
let splitAt n xs = (Seq.truncate n xs, if Seq.length xs < n then Seq.empty else Seq.skip n xs)
let rec chunk n xs =
if Seq.isEmpty xs then Seq.empty
else
let (ys,zs) = splitAt n xs
Seq.append (Seq.singleton ys) (chunk n zs)
例如:
> chunk 10 [1..100];;
val it : seq<seq<int>> =
seq
[seq [1; 2; 3; 4; ...]; seq [11; 12; 13; 14; ...];
seq [21; 22; 23; 24; ...]; seq [31; 32; 33; 34; ...]; ...]
> chunk 5 [1..12];;
val it : seq<seq<int>> =
seq [seq [1; 2; 3; 4; ...]; seq [6; 7; 8; 9; ...]; seq [11; 12]]
答案 6 :(得分:0)
如有疑问,请使用折叠。
let split n = let one, append, empty = Seq.singleton, Seq.append, Seq.empty
Seq.fold (fun (m, cur, acc) x ->
if m = n then (1, one x, append acc (one cur))
else (m+1, append cur (one x), acc))
(0, empty, empty)
>> fun (_, cur, acc) -> append acc (one cur)
这具有完全功能的优点,但只触摸输入序列的每个元素一次(*)(与上面提出的Seq.take
+ Seq.skip
解决方案相反)。
(*)假设O(1)Seq.append。我当然希望如此。
答案 7 :(得分:0)
我发现这是最快的:
setTimeout(
function()
{
$('#header .nav').removeAttr('style');
}, 400);
即。窗口列表,压缩整数列表,删除所有重叠元素,然后删除元组的整数部分。