2012-02-07 72 views
5

假設我有一個Option[A => Boolean],一個List[A],以及我想要對該列表的子集執行的一些操作集。如果選項已設置,那麼我想先過濾列表,然後應用我的操作。如果不是,那麼我想將它應用到整個列表中。舉個例子:有條件地過濾一個序列

val a : Option[Int => Boolean] = Option((a : Int) => a % 2 == 0) 
val b = 1 to 100 

我可以很容易地做到以下幾點:

val c = if (a.isDefined) b.filter(a.get) else b 

然而,這涉及調用a.get;空調讓我無法做到這一點!我也可以這樣做:

val c = b.filter(a.getOrElse(_ => true)) 

這感覺更好,但現在我堅持一個第二(雖然簡單)操作而進行我的序列的每個元素。我可以希望它會被優化,但這仍然不完美。

我想要的是缺少任何缺陷的東西。感覺應該有一個很好的方式來實現它 - 任何想法?

回答

10

你只需要使用普通的選項處理方法:

a.map(b.filter).getOrElse(b) 
+0

是的;出於某種原因,我沒有看到扭轉局勢的想法。這非常整齊。 – Submonoid 2012-02-07 17:35:11

+0

爲什麼需要將模式「option.map()。getOrElse()」添加到標準庫中的另一個原因。它總是我皮條客的第一件事之一。 – 2012-02-07 17:38:11

+2

@DaveGriffith - 確實。我也有,叫'fold'(就像Scalaz),語法爲'o.map(f).getOrElse(b)'成爲'o.fold(b)(f)'。 – 2012-02-07 18:29:31

1

如何更改到:

val a : Option[Seq[Int] => Seq[Int]] = Option((a : Seq[Int]) => a.filter(_ % 2 == 0)) 
val b = 1 to 100 
val c = a.getOrElse((f:Seq[Int]) => f)(b) 

(注意我沒有試過以上,但它應該給你的想法)

+0

啊,這看起來不錯!將情況翻轉以將列表應用於選項而不是選項列表的想法使情況更好!知道我錯過了一些東西。 – Submonoid 2012-02-07 17:32:02

3

爲雷克斯·科爾的解決方案基本相同,但使用從Scalaz fold使其稍微更簡潔。通常,x.fold(f, g)x.map(f).getOrElse(g)

scala> import scalaz._, Scalaz._ 
import scalaz._ 
import Scalaz._ 

scala> a.fold(b.filter, b) 
res112: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100) 
1

我個人比較喜歡模式匹配的清晰度;它是從你的散文描述到代碼的非常直接的翻譯:

val c = a match { 
    case Some(f) => b.filter(f) 
    case None => b 
}