带有Option参数的二元运算符

时间:2012-05-16 11:55:51

标签: scala

在scala中,如何定义两个Option参数的加法?具体来说,让我们说它们是Int类型的包装器(我实际上使用的是双打图,但这个例子更简单)。

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

  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
    }
  }

编辑添加:

在我的实际问题中,我正在添加两个映射,这些映射是稀疏矢量的替换。因此,无案例返回Map [Int,Double],而+实际上是++(在stackoverflow.com/a/7080321/614684处调整)

5 个答案:

答案 0 :(得分:26)

当你意识到自己可以站在巨人的肩膀上并利用常见的抽象和为使用它们而构建的库时,你会发现生活变得容易多了。为此,这个问题基本上就是处理问题 monoids (有关详情,请参阅下面的相关问题),相关资料库名为scalaz

使用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)

更有用的是,它适用于置于Foldable容器内的任意数量:

def add[M: Monoid, F: Foldable](as: F[Option[M]]) = ~as.asMA.sum

请注意,除了显而易见的IntStringBoolean之外,一些相当有用的幺半群是:

  1. Map[A, B: Monoid]
  2. A => (B: Monoid)
  3. Option[A: Monoid]
  4. 事实上,提取自己的方法几乎不值得:

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

    一些相关问题

    Q值。什么是幺半群?

      

    monoid是M类型,在此操作下存在关联二元操作(M, M) => M和标识I,所有mplus(m, I) == m == mplus(I, m) m类型M

    Q值。什么是|+|

      

    这只是mplus二进制操作的scalaz简写(或ASCII疯狂,ymmv)

    Q值。什么是~

      

    这是一个一元的运营商意义"或身份"如果Option[M]是一个幺半群,则scalaz库将其改为(使用scala的隐式转换)M。显然非空选项会返回其内容;一个空的选项被幺半群的身份所取代。

    Q值。什么是asMA.sum

      

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

    一些参考文献

    • SlidesVideo我发表的演讲给出了在野外使用幺半群的实际例子

答案 1 :(得分:10)

def addOpts(xs: Option[Int]*) = xs.flatten.sum

这适用于任意数量的输入。

答案 2 :(得分:5)

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

  def addOpt(a:Option[Int], b:Option[Int]) = {
    a.getOrElse(0) + b.getOrElse(0)
  }

答案 3 :(得分:4)

(根据要求在答案中重复上述评论)

您没有以正确的方式提取选项的内容。当您与case Some(x)匹配时,x是该选项中的值(类型Int),您不会在其上调用get。只是做

case Some(x) => x 

无论如何,如果你想要内容或默认,a.getOrElse(0)更方便

答案 4 :(得分:0)

def addOpt(ao: Option[Int], bo: Option[Int]) =
    for {
        a <- ao
        b <- bo
    } yield a + b