Вот более идиоматический способ сделать это. На самом деле, это однострочник; Я просто выровнял его для лучшей читабельности.
let Input = [ "Lorem"; "ipsum"; "dolor"; "set"; "amet"; "consectetuer";
"adipiscing"; "elit"; "Aenean"; "commodo"; "ligula"; "eget";
"dolor"; "Aenean"; "massa" ]
// Short solution that does not support more than two values
let Output1 =
Input
|> List.fold
(fun (i, l1, l2) x ->
if i=4 then 0, None, (l1.Value, x)::l2
elif i=1 then i+1, Some x, l2
else i+1, l1, l2
)
(0, None, [])
|> fun (_, _, elem) -> elem
|> List.rev
Идея
Общая идея основана на трех шагах:
- Разделение списка на
List
кортежей, берущих 2-ю и 5-ю строки. ВНИМАНИЕ Если исходная длина данных не является множителем 5, завершающий элемент будет потерян.
- Отфильтровать временные данные из triple, взяв третий элемент, что является нашей основной целью;
- Переворачивание списка.
Объяснение
Первая линия самая сложная.
Давайте определим наше состояние. Это будет тройка порядкового номера, string option
, содержащая строки ##2, 7 и т. д., и "внешняя" (string*string) list
, которая добавляется, когда мы встречаем элементы ##5, 10 и т. д.
Функция поместит 2-й, 7-й и т. д. элементы во «внутренний» string option
или, если i
равно 5, 10 и т. д., сформирует кортеж и добавит его во «внешний» List
(отбрасывая внутреннее значение для ясности).
Мы используем List.fold
, поэтому окончательный список нужно перевернуть.
Исходное состояние — это тройка (0, None, []). More info on
List.fold` в MSDN< /а>.
Вторая строка просто берет третий элемент из triple. Я сделал это функцией, позволяющей связывать цепочки.
Третья строка переворачивает List
из-за природы оператора ::
.
По длине исходного списка. Если он нашел "2-й" элемент, но не достиг "5-го", значение имеет второй элемент triple. Вы можете обнаружить ошибочную ситуацию, проверив ее:
...
|> fun (_, temp, elem) ->
if temp.IsSome
then failwith "Data length must be a multiplier of 5"
else elem
...
Вот немного более длинный код, который поддерживает более двух элементов:
let Output2 =
Input
|> List.foldBack
(fun x (i, l1, l2) ->
if i = 4
then 0, [], (x::l1)::l2
else i+1, x::l1, l2
)
<| (0, [], [])
|> fun (_, _, elem) -> elem
|> List.choose
(function
| [_; first; _; _; second] -> Some (first, second)
| _-> None
)
Обратите внимание, что этот вариант не удаляет элементы во время первого вызова, поэтому вы можете получить более двух элементов.
ВАЖНО. Список обрабатывается в обратном порядке, поэтому индекс элемента рассчитывается с конца ввода. Вы можете изменить его на List.fold
в стоимости или еще раз перевернуть список, как в Output1
.
Обратите внимание на оператор обратной привязки <|
из-за подписи List.foldBack
.
Вы можете проверить наличие ошибок аналогичным образом: проверив, не пуст ли "внутренний" список.
person
bytebuster
schedule
02.07.2012