У меня есть эволюция трех решений. Ни один из них не сохраняет порядок элементов ввода, что, надо надеяться, в порядке.
Мое первое решение довольно некрасиво (используя ref-ячейки):
//[[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 в первом решении, но, следовательно, вынуждает использовать прямой доступ 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, что позволяет наиболее элегантному решению использовать сопоставление с образцом, как указывает Томас, в 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