如何在 F# 中乘以两个(双选项)

How to multiply two (double option)s in F#

我的代码包含很多双选项类型;到目前为止,我一直在非常成功地使用 Option.map 函数来消除必须在整个地方匹配 Some 和 None 并将它们视为提升类型的需要,但我不确定该怎么做以下场景:

let multiplyTwoOptions option1 option2 : double option =
  if not (option1.IsSome && option2.IsSome) then None
  else Some (option1.Value * option2.Value)

我已经 read 你不应该以这种方式使用 IsSome,但替代方案(据我所知,按顺序对两者进行模式匹配,似乎很冗长)。我对 F# 还是很陌生,所以想知道是否有更惯用的方法?我觉得也许我需要像 Option.fold2 这样的东西来同时处理两个选项,但没有一个。

模式可以嵌套,这是它们的强大之处。在这种特殊情况下,您可以对元组进行模式匹配:

match option1, option2 with
| Some x, Some y -> Some (x * y)
| _ -> None

正确答案如下:

https://fsharpforfunandprofit.com/posts/elevated-world/#apply

如果需要一些代码,则可以归结为以下示例:

module Option =

    // The apply function for Options
    let apply fOpt xOpt = 
        match fOpt,xOpt with
        | Some f, Some x -> Some (f x)
        | _ -> None


let (<!>) = Option.map
let (<*>) = Option.apply

let a = Some(4)
let b = Some(5)

let  multiplication = (*)

//Some multiplication function applied on a and resulting function applied on b
let res1 = Some(multiplication) <*> a <*> b
let res2 = Some(*) <*> a <*> b

//Map a onto multiplication function and resulting function applied on b
let res3 = multiplication <!> a <*> b
let res4 = (*) <!> a <*> b


val res1 : int option = Some 20
val res2 : int option = Some 20
val res3 : int option = Some 20
val res4 : int option = Some 20

//The following is without any options to try to clarify the above

let op = (*) //multiplication
//let partialRes = (*) 4
let partialRes = op 4 //make function for multiplying param with 4
let fullres = partialRes 5 //use function for multiplying with 4

val op : (int -> int -> int)
val partialRes : (int -> int)
val fullres : int = 20

之所以这样说,是因为上面的方法可以在 Wlaschin 所谓的 "elevated world" 中进出,或者在这种情况下是 Option,并混合两者的东西。有点儿。阅读 Wlaschin 撰写的完整网站或书籍。

不需要做任何以Option为参数的函数,包装和解包可以一劳永逸。

正如上面的代码所示(无耻地从link偷来的,并进行了一些重写),理查德需要的功能是:

Option.apply

是的,这些符号可能会造成混淆,尤其是因为我们在这里讨论的是乘法或 *,但是映射 和应用 <*> 的这些符号有点 'standard'。

关于如何阅读代码,我认为代码中的注释或多或少是正确的。

是的,我可能需要改进我的教学风格 ;-)

您的 fold2 想法并没有错。即使它不是标准库的一部分,您也可以轻松地自己实现这些功能。

这是 bind2:

module Option =
    let bind2 f a b =
        match a, b with
        | Some a, Some b -> f a b
        | _, _ -> None 

我也看到 bind3 使用过,但这可能有点过时了。我怀疑超过 3 个参数有多少实际用途。您可以按照类似的方案实现 mapiterfold 的不同参数。

这不像使用应用函子那样健壮或形式上优雅,但它解决了问题而没有引入太多概念开销。

我会选择 match,但作为替代方案,您也可以使用 https://www.nuget.org/packages/FSharpx.Extras/

中的 maybe 表达式
let multiplyTwoOptions option1 option2 : double option =
  maybe {
    let! x = option1
    let! y = option2
    return x * y
  }

虽然对于一组已知的输入,match 更直接,但您会注意到 maybe 表达式更适合较长的临时表达式:

maybe {
  let! a = getAOption()
  let! b = getBOption a
  let! c = getCOption a b
  return! getFinalOption a b c
}

match getAOption() with
| None -> None
| Some a ->
  match getBOption a with
  | None -> None
  | Some b -> 
    match getCOption a b with
    | None -> None
    | Some c -> getFinalOption a b c

这也比应用函子风格有优势,例如函数生成 b 可以依赖于 ac 可以依赖于 ab,等等,这在应用风格中是不可能的。计算表达式语法也比只有原始运算符的应用函子更容易理解。