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 
}