带有Option参数的二元运算符

Jas*_*ond 7 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
    }
  }
Run Code Online (Sandbox Code Playgroud)

编辑添加:

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

oxb*_*kes 26

当你意识到你可以站在巨人的肩膀上并利用常见的抽象和为使用它们而构建的库时,你可能会发现生活变得容易多了.为此,这个问题基本上是关于处理 幺半群(请参阅下面的相关问题以获取更多相关信息),并且所讨论的库称为scalaz.

使用scalaz FP,这只是:

def add(a: Option[Int], b: Option[Int]) = ~(a |+| b)
Run Code Online (Sandbox Code Playgroud)

对于任何幺半群M来说,这更有用:

def add[M: Monoid](a: Option[M], b: Option[M]) = ~(a |+| b)
Run Code Online (Sandbox Code Playgroud)

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

def add[M: Monoid, F: Foldable](as: F[Option[M]]) = ~as.asMA.sum
Run Code Online (Sandbox Code Playgroud)

需要注意的是一些相当有用类群,除了明显的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))
Run Code Online (Sandbox Code Playgroud)

一些相关的问题

问:什么是幺半群?

monoid是在此操作下M存在关联二进制操作(M, M) => M和标识的类型I,mplus(m, I) == m == mplus(I, m)以便对于所有m类型M

问:什么是|+|

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

问:什么是~

它是一个一元运算符,意思是"或身份",它被scalaz库改进(使用scala的隐式转换)Option[M]if if M是一个monoid.显然非空选项会返回其内容; 一个空选项被monoid的标识所取代.

问:什么是asMA.sum

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

一些参考文献

  • 我给出的谈话的幻灯片视频给出了在野外使用幺半群的实际例子

  • ```做什么?我想你应该更好地解释一下这些成分. (5认同)
  • 对于我们这些使用scalaz 7的人,我相信相当于`~res0.suml` (2认同)

Lui*_*hys 10

def addOpts(xs: Option[Int]*) = xs.flatten.sum
Run Code Online (Sandbox Code Playgroud)

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


Mau*_*res 5

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

  def addOpt(a:Option[Int], b:Option[Int]) = {
    a.getOrElse(0) + b.getOrElse(0)
  }
Run Code Online (Sandbox Code Playgroud)

  • 你能用这些信息更新你的问题吗? (3认同)

Did*_*ont 4

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

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

case Some(x) => x 
Run Code Online (Sandbox Code Playgroud)

反正如果你想要内容还是默认的a.getOrElse(0)比较方便