我想将这样的功能重写为F#:
zipWith' :: (a -> b -> c) -> (a -> c) -> (b -> c) -> [a] -> [b] -> [c]
zipWith' _ _ h [] bs = h `map` bs
zipWith' _ g _ as [] = g `map` as
zipWith' f g h (a:as) (b:bs) = f a b:zipWith f g h as bs
我的第一次尝试是:
let inline private map2' (xs : seq<'T>) (ys : seq<'U>) (f : 'T -> 'U -> 'S) (g : 'T -> 'S) (h : 'U -> 'S) =
let xenum = xs.GetEnumerator()
let yenum = ys.GetEnumerator()
seq {
let rec rest (zenum : IEnumerator<'A>) (i : 'A -> 'S) =
seq {
yield i(zenum.Current)
if zenum.MoveNext() then yield! (rest zenum i) else zenum.Dispose()
}
let rec merge () =
seq {
if xenum.MoveNext()
then
if yenum.MoveNext()
then yield (f xenum.Current yenum.Current); yield! (merge ())
else yenum.Dispose(); yield! (rest xenum g)
else
xenum.Dispose()
if yenum.MoveNext()
then yield! (rest yenum h)
else yenum.Dispose()
}
yield! (merge ())
}
然而,它很难被认为是惯用的。我听说过LazyList
,但我无法在任何地方找到它。
答案 0 :(得分:3)
正如Brian所说,F#在PowerPack中提供了一个常见的Haskell风格的惰性列表,因此您可以使用它。不幸的是,使用标准F#序列表达式来表达这种事情是没有好办法的,因为它们只能表达使用for
从单个序列读取数据的计算(在您的情况下,您需要从中读取)多个序列)。
然而,可以编写一个计算(类似于seq { .. }
)来处理IEnumerator<T>
- 这是一个必要的计算,修改后面的枚举器,但它可以用于编码seq
不够好时的模式。我正在计划博客,但与此同时,您可以get it here(代码还包括您问题的解决方案)。
然后你可以这样写:
// 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解决方案的结构,这使得这种方法非常有吸引力,但您仍然可以直接使用序列,而无需将数据复制到其他数据结构。
答案 1 :(得分:2)
LazyList位于F#PowerPack中。你可能需要它来更优雅地写这个。鉴于您的首次尝试代码看起来有多好,我希望您在编写LazyList版本时可以毫无困难。
答案 2 :(得分:1)
我建议:
let forever xs =
Seq.append (Seq.map Some xs) (Seq.initInfinite (fun _ -> None))
let zipWith f g h xs ys =
Seq.zip (forever xs) (forever ys)
|> Seq.takeWhile (fun (x, y) -> Option.isSome x || Option.isSome y)
|> Seq.map ( function
| (Some x, Some y) -> f x y
| (Some x, None ) -> g x
| (None , Some y) -> h y
| _ -> failwith "quite unexpected !" )