2010-05-15 53 views
1

我想這樣的功能改寫成F#:在F#中修改map2(沒有列表的截斷) - 如何通俗地做到這一點?

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,但我無法在任何地方找到它。

回答

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())) 

代碼幾乎複製原始哈斯克爾解決方案的結構,使得這種方法非常有吸引力的核心部分,但你仍然可以使用序列,而不:

然後,你可以這樣寫將數據複製到其他數據結構。

2

LazyList位於F#PowerPack。您可能需要它來更優雅地編寫它。鑑於您的第一次嘗試代碼看起來有多好,我希望您在編寫LazyList版本時不會遇到麻煩。

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 !")