Как упоминал Брайан, F # предоставляет обычный ленивый список в стиле Haskell в PowerPack, так что вы можете использовать его. К сожалению, не существует хорошего способа выразить подобные вещи с помощью стандартных выражений последовательности F #, потому что они могут выражать только вычисления, которые читают данные из одной последовательности, используя for
(в вашем случае вам нужно будет читать из нескольких последовательностей ).
Однако можно написать вычисление (аналогично seq { .. }
) для работы с IEnumerator<T>
- это обязательное вычисление, которое изменяет счетчик за кулисами, но его можно использовать для шаблонов кодирования, когда seq
недостаточно хорош Я планирую написать об этом в блоге, но пока вы можете получить его здесь (код также включает решение вашей проблемы).
Тогда вы можете написать это:
// Zip using specified functions for sequences
let zipWithFun f g h (a:seq<_>) (b:seq<_>) =
// Local helper function that works with iterators (xs and ys)
let rec zipWithFunE xs ys = iter {
// Try to get first element from both iterators (mutates the iterators!)
let! x = xs
let! y = ys
match x, y with
| Some(x), Some(y) ->
// If both produced value, then combine values using 'f' & continue
yield f (x, y)
yield! zipWithFunE xs ys
// If only one produced value, yield the value and then return the
// remaining values projected using one of the functions
| Some(rest), _ ->
yield g rest
yield! ys |> Enumerator.map g
| _, Some(rest) ->
yield g rest
yield! ys |> Enumerator.map g
| _ -> () }
// Construct a 'seq' value from a function that processes enumerators
Enumerator.toSeq (fun () ->
zipE (a.GetEnumerator()) (b.GetEnumerator()))
Основная часть кода в значительной степени копирует структуру исходного решения Haskell, что делает этот подход очень привлекательным, но вы все равно можете использовать последовательности напрямую, без копирования данных в какую-либо другую структуру данных.