如何在 Scala 中组合 Option 值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2712390/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:06:52  来源:igfitidea点击:

How to combine Option values in Scala?

scalatype-conversionscalaz

提问by Jeff

I want to be able to apply an operation f: (T,T) => Tto Option[T]values in Scala. I want the result to be Noneif any of the two values is None.

我希望能够应用的操作f: (T,T) => T,以Option[T]在斯卡拉值。我希望结果是None两个值中的任何一个是None.

More specifically, I want to know if there is a shorter way to do the following:

更具体地说,我想知道是否有更短的方法来执行以下操作:

def opt_apply[T](f: (T,T) => T, x: Option[T], y: Option[T]): Option[T] = {
  (x,y) match {
    case (Some(u),Some(v)) => Some(f(u,v))
    case _ => None
  }
}

I have tryied (x zip y) map {case (u,v) => f(u,v)}but the result is an Iterator[T]not an Option[T].

我已经尝试过,(x zip y) map {case (u,v) => f(u,v)}但结果Iterator[T]不是Option[T].

回答by missingfaktor

scala> val (x, y) = (Some(4), Some(9))
x: Some[Int] = Some(4)
y: Some[Int] = Some(9)

scala> def f(x: Int, y: Int) = Math.max(x, y)
f: (x: Int,y: Int)Int

scala> for { x0 <- x; y0 <- y } yield f(x0, y0)
res26: Option[Int] = Some(9)

scala> val x = None
x: None.type = None

scala> for { x0 <- x; y0 <- y } yield f(x0, y0)
res27: Option[Int] = None

回答by retronym

@RahulG's answer exploits the fact that Optionis a monad (even though there is no type to represent this in the Scala library). The compiler expands the forcomprehension to the following:

@RahulG的回答利用Option了 monad的事实(即使在 Scala 库中没有表示它的类型)。编译器将for理解扩展为以下内容:

def a: Option[Int]
def b: Option[Int]
val calc: Option[Int] = a flatMap {aa => b map {bb => aa + bb}}

You can also treat it as an applicative functor, with some help from Scalaz:

在 Scalaz 的帮助下,您也可以将其视为应用函子:

import scalaz._
import Scalaz._

def a: Option[Int]
def b: Option[Int]
val calc: Option[Int] = (a ? b) {_ + _}

A key difference is that in the monadic calculation, a failure (that is, None) of calculation ashort circuits the evaluation. In the applicative style, both aand bare evaluated, and if both are Somes, the pure function is called. You can also see that in the monadic calculation, the value aacould have been used in the calculation b; in the applicative version, bcannot depend on the result of a.

一个关键的区别在于,在 monadic 计算中,计算失败(即Nonea会使评估短路。在 applicative 风格中,ab都被评估,如果两者都是Somes,则调用纯函数。您还可以看到,在 monadic 计算中,该值aa可能已用于计算b;在应用版本中,b不能依赖于 的结果a

回答by oxbow_lakes

I have a slightly older version of scalazthan retronymbut the following works for me as an example and is generalizable for the case where you have 3 types T, U, Vand not just one:

我有一个稍微旧版本的scalaz返璞词,但对我来说,以下的作品作为一个例子,是普及的,你有3种类型的情况下T, U, V,而不是只有一个:

def main(args: Array[String]) {
  import scalaz._
  import Scalaz._

  val opt1 = some(4.0) //Option[Double]
  val opt2 = some(3)   //Option[Int]

  val f: (Double, Int) => String = (d, i) => "[%d and %.2f]".format(i, d)

  val res = (opt1 <|*|> opt2).map(f.tupled)
  println(res) //Some([3 and 4.00])
}

I can then add:

然后我可以添加:

val opt3 = none[Int]
val res2 = (opt1 <|*|> opt3).map(f.tupled)
println(res2) //None

回答by user142435

You can use for comprehensions:

您可以用于理解:

def opt_apply[T](f: (T,T) => T, x: Option[T], y: Option[T]): Option[T] = 
     for (xp <- x; yp <- y) yield (f(xp,yp))

Which is sugar for:

哪个是糖:

x flatMap {xp => y map {yp => f(xp, yp)}}

This is also possible due to Option being a Monad

这也是可能的,因为 Option 是一个 Monad

回答by Levi Ramsey

def optApply[A,B,C](f: (A, B) => C, a: Option[A], b: Option[B]): Option[C] =
  a.zip(b).headOption.map { tup => f.tupled(tup) }

a.zip(b)does result in an Iterable[(A, B)] (with, because it's from Options, at most one element). headOptionthen returns the first element as an Option.

a.zip(b)确实会产生一个 Iterable[(A, B)] (因为它来自 Options,最多一个元素)。 headOption然后将第一个元素作为选项返回。

回答by Xavier Guihot

Starting Scala 2.13, Option#zipcan be applied to another Optionto return an Option(in earlier versions, it would have returned an Iterable); thus:

开始Scala 2.13,Option#zip可以应用于另一个Option返回 an Option(在早期版本中,它会返回 an Iterable);因此:

def optApply[T](f: (T,T) => T, a: Option[T], b: Option[T]): Option[T] =
  a.zip(b).map(f.tupled)

where the behavior of zipis:

其中的行为zip是:

Some(2).zip(Some(3)) // Some((2, 3))
Some(2).zip(None)    // None
None.zip(Some(3))    // None
None.zip(None)       // None

and which can be applied as such:

并且可以这样应用:

optApply[Int]((a, b) => a max b, Some(2), Some(5)) // Some(5)
optApply[Int]((a, b) => a max b, Some(2), None)    // None