7

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

编辑添加:

在我的实际问题中,我添加了两张代表稀疏向量的地图。所以 None 情况返回 Map[Int, Double] 并且 + 实际上是 ++(在 stackoverflow.com/a/7080321/614684 进行了调整)

4

5 回答 5

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

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

  1. Map[A, B: Monoid]
  2. A => (B: Monoid)
  3. Option[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) => MI在此运算下的恒等式的类型,因此mplus(m, I) == m == mplus(I, m)对于所有m类型M

问:什么是|+|

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

问:什么是~

它是一个一元运算符,意思是“或身份”,由 scalaz 库改造(使用 scala 的隐式转换)到Option[M]ifM是一个幺半群。显然,非空选项返回其内容;一个空选项被幺半群的身份替换。

问:什么是asMA.sum

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

一些参考资料

于 2012-05-16T16:06:14.960 回答
10
def addOpts(xs: Option[Int]*) = xs.flatten.sum

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

于 2012-05-16T12:17:09.540 回答
5

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

  def addOpt(a:Option[Int], b:Option[Int]) = {
    a.getOrElse(0) + b.getOrElse(0)
  }
于 2012-05-16T12:00:33.273 回答
4

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

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

case Some(x) => x 

反正如果要内容或者默认,a.getOrElse(0)比较方便

于 2012-05-16T16:38:57.387 回答
0
def addOpt(ao: Option[Int], bo: Option[Int]) =
    for {
        a <- ao
        b <- bo
    } yield a + b
于 2012-05-16T17:37:04.297 回答