我们正在尝试从 http://www.haskell.org/all_about_monads/html/maybemonad.html 在f#中。

这个想法是在两个词典中搜索一个邮编。如果两个查找都返回结果,我们会查看第三个。

let bindM x k =
    match x with
    | Some value -> k value
    | None   -> None

let returnM x = Some x

type MaybeBuilder() =
     member this.Bind(x, k) = bindM x k
     member this.Return(x)  = returnM x
     member this.ReturnFrom(x) = x
     member this.Delay(f)   = f()

let maybe = MaybeBuilder()

//Sample dictionaries
let fullNamesDb = 
    [("Bill Gates", "billg@microsoft.com")    
     ("Bill Clinton", "bill@hope.ar.us")
     ("Michael Jackson", "mj@wonderland.org")
     ("No Pref Guy", "guy@nopref.org")]
       |> Map.ofList

let nickNamesDb =
    [("billy", "billg@microsoft.com")
     ("slick willy", "bill@hope.ar.us")
     ("jacko", "mj@wonderland.org") ]
        |> Map.ofList

let prefsDb =
    [("billg@microsoft.com", "HTML")
     ("bill@hope.ar.us", "Plain")
     ("mj@wonderland.org", "HTML")]
        |> Map.ofList


let mplus m1 m2 = if m1 <> None then m1 else m2
let (+) = mplus

let lookUp name = maybe {
    let! combined = fullNamesDb.TryFind name + nickNamesDb.TryFind name
    return! prefsDb.TryFind combined
}

let billGatesPref = lookUp "Bill Gates" |> printfn "%A" // Some "HTML"
let billyPref = lookUp "billy" |> printfn "%A" // Some "HTML"
let billClintonPref = lookUp "Bill Clinton" |> printfn "%A" // Some "Plain"
let steffenPref = lookUp "Steffen" |> printfn "%A" // None
let noPref = lookUp "No Pref Guy" |> printfn "%A" // None

System.Console.ReadKey() |> ignore

问题在于,即使第一个返回结果,我们也执行第二个查找。关于Haskell的好处在这里,它可以评估懒惰。现在,我们在F#中寻找类似的东西。我们尝试了以下操作,但看起来很丑陋,似乎打破了封装建筑商中逻辑的想法:

let mplus m1 m2 = if m1 <> None then m1 else m2()
let (+) = mplus

let lookUp name = maybe {
    let! combined = fullNamesDb.TryFind name + fun _ -> nickNamesDb.TryFind name
    return! prefsDb.TryFind combined
}

有更好的解决方案吗?

问候,福基

有帮助吗?

解决方案

您可以实现在Baybuilder中运行/组合的其他方法,因此结果应为以下内容:

let bindM x k =
match x with
| Some value -> k value
| None   -> None

let returnM x = Some x

type MaybeBuilder() =
    member this.Bind(x, k) = bindM x k
    member this.Return(x)  = returnM x
    member this.ReturnFrom(x) = x
    member this.Delay(f)   = f
    member this.Combine(a, b) = if Option.isSome a then a else b()
    member this.Run(f) = f()

let maybe = MaybeBuilder()

//Sample dictionaries (the same with original sample)
let fullNamesDb = ... 
let nickNamesDb = ...
let prefsDb = ....

let lookUp name = 
    let findName m = maybe {
        let! v = Map.tryFind name m
        return! prefsDb.TryFind v 
        }

    maybe {
        return! findName fullNamesDb
        return! findName nickNamesDb 
    }


let billGatesPref = lookUp "Bill Gates" |> printfn "%A" // Some "HTML"
let billyPref = lookUp "billy" |> printfn "%A" // Some "HTML"
let billClintonPref = lookUp "Bill Clinton" |> printfn "%A" // Some "Plain"
let steffenPref = lookUp "Steffen" |> printfn "%A" // None
let noPref = lookUp "No Pref Guy" |> printfn "%A" // None

其他提示

总是有 Lazy, ,这实际上是您在这里拥有的,但具有不同的语法:

let mplus m1 (m2 : Lazy<'a option>) =
    match m1 with
    | Some _ as m -> m
    | None -> m2.Force()

let (+) = mplus

let lookUp name = maybe {
    let! combined = fullNamesDb.TryFind name + lazy (nickNamesDb.TryFind name)
    return! prefsDb.TryFind combined
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top