Scala 带选项参数的二元运算符

Scala 带选项参数的二元运算符,scala,Scala,在scala中,如何定义两个选项参数上的加法?具体来说,让我们假设它们是Int类型的包装器(我实际上正在处理double的映射,但这个示例更简单) 我尝试了以下操作,但它只是给了我一个错误: def addOpt(a:Option[Int], b:Option[Int]) = { a match { case Some(x) => x.get case None => 0 } + b match { case Some(y) =

在scala中,如何定义两个选项参数上的加法?具体来说,让我们假设它们是
Int
类型的包装器(我实际上正在处理double的映射,但这个示例更简单)

我尝试了以下操作,但它只是给了我一个错误:

  def addOpt(a:Option[Int], b:Option[Int]) = {
    a match {
      case Some(x) => x.get
      case None => 0
    } + b match {
      case Some(y) => y.get
      case None => 0
    }
  }
编辑以添加:


在我的实际问题中,我添加了两个贴图,它们代表稀疏向量。因此None case返回Map[Int,Double],而+实际上是一个++(在stackoverflow.com/a/7080321/614684上有调整)

如果它们都默认为0,则不需要模式匹配:

  def addOpt(a:Option[Int], b:Option[Int]) = {
    a.getOrElse(0) + b.getOrElse(0)
  }
这将适用于任意数量的输入。

幺半群 当你意识到你可以站在巨人的肩膀上,利用共同的抽象和为使用它们而建立的库时,你可能会发现生活变得容易多了。为此,这个问题基本上是关于处理 幺半群(有关这方面的更多信息,请参见下面的相关问题),该库被称为

使用scalaz FP,这只是:

def add(a: Option[Int], b: Option[Int]) = ~(a |+| b)
更重要的是,这适用于任何幺半群M:

def add[M: Monoid](a: Option[M], b: Option[M]) = ~(a |+| b)
更有用的是,它可以处理放置在可折叠容器中的任意数量的代码:

def add[M: Monoid, F: Foldable](as: F[Option[M]]) = ~as.asMA.sum
请注意,除了明显的
Int
String
Boolean
之外,还有一些非常有用的幺半群:

  • Map[A,B:Monoid]
  • A=>(B:Monoid)
  • 选项[A:Monoid]
  • 事实上,提取您自己的方法几乎不值得:

    scala> some(some(some(1))) #:: some(some(some(2))) #:: Stream.empty
    res0: scala.collection.immutable.Stream[Option[Option[Option[Int]]]] = Stream(Some(Some(Some(1))), ?)
    
    scala> ~res0.asMA.sum
    res1: Option[Option[Int]] = Some(Some(3))
    

    一些相关问题 什么是幺半群

    幺半群是一种类型
    M
    ,对于该类型,在该操作下存在关联二进制操作
    (M,M)=>M
    和标识
    I
    ,使得
    mplus(M,I)==M==mplus(I,M)
    对于类型
    M
    的所有
    M

    什么是
    |+|

    这只是
    mplus
    二进制操作的scalaz速记(或ASCII-madness,ymmv)

    什么是
    ~

    如果
    M
    是幺半群,它是一个一元运算符,意思是“或标识”,scalaz库将其改装为
    Option[M]
    (使用scala的隐式转换)。显然,非空选项返回其内容;空选项将替换为幺半群的标识

    问:
    asMA.sum
    是什么

    foldLeft
    基本上是一种可以折叠的数据结构(例如
    foldLeft
    )。回想一下,
    foldLeft
    采用种子值和操作组成连续计算。在求和幺半群的情况下,种子值是标识
    I
    ,操作是
    mplus
    。因此,您可以在可折叠的[M:Monoid]上调用
    asMA.sum
    。您可能需要使用
    asMA
    ,因为名称与标准库的
    sum
    方法冲突

    一些参考资料
    • 我的一次演讲给出了在野外使用幺半群的实际例子
    (根据要求在回答中重复上述评论)


    您没有以正确的方式提取选项的内容。当您与
    case Some(x)
    匹配时,
    x
    是选项(键入
    Int
    )中的值,您不会调用
    get
    。照办

    case Some(x) => x 
    
    无论如何,如果您想要内容或默认值,
    a.getOrElse(0)
    更方便

    def addOpt(ao:Option[Int],bo:Option[Int])=
    
    def addOpt(ao: Option[Int], bo: Option[Int]) =
        for {
            a <- ao
            b <- bo
        } yield a + b
    
    为了{
    a在我的实际问题中,它们没有。我正在添加两个映射,它们是稀疏向量的替代。如果两个映射都没有默认值,那么在这种情况下就不能使用选项,因为它们中的一个或两个都可能是未定义的。如果我将它们默认为Map[Int,Double](这就是它们的实际值),而
    +
    实际上是
    +
    (在.FWIW处进行了调整,为了使这项工作适用于
    Map[Int,Double]
    ,它只需
    def addOpts(xs:Option[Map[Int,Double]]*)=xs.flatte.foldLeft(Map.empty[Int,Double]){{code++}
    或者同时适用于这两者的scalaz版本:
    def addOpts[T:Monoid](xs:Option[T]*)=xs.flatte.asMA.sum
    您没有以正确的方式提取选项的内容。当您匹配case Some(x)时,x是选项内的值(键入Int),您不会调用get。case Some(x)=>x。总之,如果您想要内容或默认值,a.getOrElse(0)更方便。@didierd谢谢!这是我需要的答案。你能转换成一个答案吗?我会选择你的答案。强制性参考:什么是
    ~
    做的?我认为你应该更好地解释成分。对于我们中使用scalaz 7的人,我相信等效的答案是,
    ~res0.suml
    谢谢!这非常有用。我很抱歉尽管使用@didierd来指出scala抱怨的原因。@JasonMond仔细看看这个幺半群的东西,它很有用,我保证!有人应该在类路径上用scalaz设置一个“simplyscala”,以便于原型化我认为
    ~
    应该是“或零”,而不是“或标识”。这返回
    选项[Int]
    而不是
    Int
    。此外,如果
    ao
    bo
    None
    ,则返回
    None
    ,这不是期望的结果。你是对的,它不会产生OP代码的预期结果,但是我要说它确实解决了问题标题的精神。虽然它不像origi那样返回0最后回答,只需添加getOrElse即可
    def addOpt(ao: Option[Int], bo: Option[Int]) =
        for {
            a <- ao
            b <- bo
        } yield a + b