2010-04-26 47 views
26

我希望能够在Scala中应用操作f: (T,T) => TOption[T]值。如果两个值中的任何一个值为None,我希望结果为None如何在Scala中结合选项值?

更具体地说,我想知道是否有一个较短的方式来做到以下几点:我已经tryied (x zip y) map {case (u,v) => f(u,v)}

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

但结果是Iterator[T]不是Option[T]

任何帮助将不胜感激。谢谢。

回答

28
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 
+0

errrr,这应该更新正确http://docs.scala-lang.org/style/control-structures.html,因为它应该是{x0 < - x; y0 < - y)},我想,对吧? – 2014-10-15 17:50:50

+0

@DeanHiller,嗯是的。风格指南在我想的时候不存在。 :) – missingfaktor 2014-10-16 10:16:23

17

@RahulG的回答是利用这样的事实Option是一个单子(即使是没有类型在Scala库,以表示此)。编译器扩展for理解以下几点:

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

你也可以把它当作一个适用函子,从Scalaz一些帮助:

import scalaz._ 
import Scalaz._ 

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

一个关键的区别是,在一元计算,计算a的故障(即None)短路评估。在应用风格中,评估ab,如果两者均为Some s,则调用纯函数。您还可以看到,在一次计算中,值aa可能已用于计算b;在应用版本中,b不能取决于a的结果。

+0

这种方法的ASCII码是否等于'<|*|>'? – 2010-04-26 11:51:47

+1

'<*>'允许您提供'纯'功能,在这种情况下'(a,b)=> a + b'。 '<|*|>'是使用'Tuple2.apply'作为纯函数的一个方便。 “⊛”实际上比arity-2更普遍一些,你可以写成(a⊛b a⊛b){_ + _ + _ + _}'。这是一个小实验,因此,还没有ASCII别名。 – retronym 2010-04-26 12:02:50

+0

Typo,我的意思是:'(a⊛b⊛a⊛b){_ + _ + _ + _}' – retronym 2010-04-26 12:25:02

3

我有一个稍微旧版本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]) 
} 

我可以再添加:

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

用'<*>'替换'<|*|>'以避免创建临时元组,并直接使用'f'。 – retronym 2010-04-26 15:19:09

+0

不适用于不同的参数类型,我认为 – 2010-04-26 17:34:11

+0

糟糕,我的意思是'<**>' – retronym 2010-04-26 18:45:04

1

您可以使用内涵:

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

哪个糖:

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

这也是可能的,因为选项是一个单子

+3

这很奇怪。当我发布这个消息时,我没有看到@ RahulG的回答。 – user142435 2010-04-26 14:52:26

0
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)不会导致可迭代[(A,B)](带有,因为它来自选项,最多只有一个元素)。 headOption然后将第一个元素作为选项返回。