可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to be able to apply an operation f: (T,T) => T
to Option[T]
values in Scala. I want the result to be None
if any of the two values is None
.
More specifically, I want to know if is there a shorter way to do the following:
def opt_apply[T](f: (T,T) => V, 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]
.
Any help will be appreciated. Thanks.
回答1:
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
回答2:
@RahulG's answer exploits the fact that Option
is a monad (even though there is no type to represent this in the Scala library). The compiler expands the for
comprehension to the following:
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:
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 a
short circuits the evaluation. In the applicative style, both a
and b
are evaluated, and if both are Some
s, the pure function is called. You can also see that in the monadic calculation, the value aa
could have been used in the calculation b
; in the applicative version, b
cannot depend on the result of a
.
回答3:
I have a slightly older version of scalaz than retronym but the following works for me as an example and is generalizable for the case where you have 3 types T, U, V
and not just one:
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
回答4:
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
回答5:
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). headOption
then returns the first element as an Option.