如何在F#中将两个(double option)相乘。

6

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

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

看到过不应该以这种方式使用IsSome,但是替代方法(就我所知,按顺序进行模式匹配似乎很冗长)。我对F#还比较陌生,想知道是否有更具代表性的方法?我觉得也许需要像Option.fold2这样的东西来同时处理两个选项,但实际上并没有。

4个回答

13

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

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

9

正确答案实际上如下:

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所称的“高层世界”或者本例中的Option内外工作,并混合两者的内容。有点像。请阅读完整网站或Wlaschin所写的书以获得更多信息。
没有必要创建任何以Option为参数的函数,一次包装和解除包装即可全部处理。
正如上面的代码所示(从链接中不知羞耻地盗取,并被部分重写),Richard需要的函数是:
Option.apply

是的,这些符号可能会让人感到困惑,尤其是因为我们在谈论乘法或*,但是map <!>和apply <*>这些符号在某种程度上是“标准”的。

我认为代码中的注释在阅读代码方面更或多或少是正确的。

而且,是的,也许我需要在教学风格上做些努力;-)


非常感谢您发布了这篇详细的文章,我需要抽出时间来仔细阅读 =) - Dutts
2
我不会在同一句话中使用“grok”和“minute”这个词;-) 尤其是在这个主题上。我仍然不理解它,需要非常仔细地考虑每一个细节,以确保我做得正确。然后我总是会想:我做对了吗... 这篇文章也是为了帮助我理解... - Helge Rene Urholm
2
不确定是否应该称其为正确的答案。应用函子几乎不是 F# 课程的一部分。 - scrwtp
如果你正在使用Options,那么它应该是这样的。但是,是的。对我来说,它很难理解,并且在实际重用方面也很困难(尽管函数式编程并不真正适合)。 - Helge Rene Urholm

3

您的fold2想法并不是错的。即使它不是标准库的一部分,您也可以轻松地自己实现这样的函数。

这里是一个bind2

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

我见过使用bind3的例子,但那可能有些勉强。我怀疑多于3个参数的实际应用场景不多。你可以按照类似的方案实现不同参数数量的mapiterfold。这种方法不如使用可应用函子那样健壮或形式上优雅,但它解决了问题而没有引入太多概念负担。

3

我会选择使用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
}

vs

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

这种写法比applicative functor风格更加优越,因为生成b的函数可以依赖于a,c可以依赖于a和b等等,这在applicative风格中不可能。计算表达式语法也比只有原始操作符的applicative functors更容易理解。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接