2010-04-26 49 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然後將第一個元素作爲選項返回。