6

I have a code such as:

val strs = List("hello", "andorra", "trab", "world")
def f1(s: String) = !s.startsWith("a")
def f2(s: String) = !s.endsWith("b")

val result = strs.filter(f1).filter(f2)

now, f1 and f2 should be applied based on a condition, such as:

val tmp1 = if (cond1) strs.filter(f1) else strs
val out  = if (cond2) tmp1.filter(f2) else tmp1

is there a nicer way to do this, without using a temporary variable tmp1?

one way would to filter based on a list of functions, such as:

val fs = List(f1 _,f2 _)
fs.foldLeft(strs)((fn, list) => list.filter(fn))

but then I would need to build a list of functions based on the conditions (and so, I would move the problem of using a temporary string list variable, to using a temporary function list variable (or I should need to use a mutable list)).

I am looking something like this (of course this does not compile, otherwise I would already have the answer to the question):

val result = 
  strs
    .if(cond1, filter(f1))
    .if(cond2, filter(f2))
4

4 回答 4

10

您可以轻松地使用隐式类来为您提供以下语法:

  val strs = List("hello", "andorra", "trab", "world")

  def f1(s: String) = !s.startsWith("a")

  def f2(s: String) = !s.endsWith("b")

  val cond1 = true
  val cond2 = true

  implicit class FilterHelper[A](l: List[A]) {
    def ifFilter(cond: Boolean, f: A => Boolean) = {
      if (cond) l.filter(f) else l
    }
  }

  strs
    .ifFilter(cond1, f1)
    .ifFilter(cond2, f2)

res1: List[String] = List(hello, world)

我会用作if方法名称,但它是保留字。

于 2013-07-01T14:20:03.093 回答
4

您可以通过对谓词函数求和来做到这一点。

观察过滤谓词 ,A => Boolean有一个追加操作:

def append[A](p1: A => Boolean, p2: A => Boolean): A => Boolean =
  a => p1(a) && p2(a)

和一个身份值:

def id[A]: A => Boolean =
  _ => true

满足对于任何谓词p: A => Boolean,的条件append(p, id) === p

这简化了基于条件包含/排除谓词的问题:如果条件为假,只需包含id谓词。它对过滤器没有影响,因为它总是返回true

总结谓词:

def sum[A](ps: List[A => Boolean]): A => Boolean =
  ps.foldLeft[A => Boolean](id)(append)

请注意,我们折叠到 上id,因此如果ps为空,我们将获得身份谓词,即,正如您所期望的那样,一个什么都不做的过滤器。

把这一切放在一起:

val predicates = List(cond1 -> f1 _, cond2 -> f2 _)

strs.filter(sum(predicates.collect { case (cond, p) if cond => p }))
// List(hello, world)

请注意,该列表strs仅被遍历一次。


现在,对于上述的 Scalaz 版本:

val predicates = List(cond1 -> f1 _, cond2 -> f2 _)

strs filter predicates.foldMap {
  case (cond, p) => cond ?? (p andThen (_.conjunction))
}
// List("hello", "world")
于 2013-07-01T14:29:42.547 回答
3

@Noah 的回答很好,如果您希望能够对列表执行任何类型的操作,您可以接受并进一步概括它,然后在给定条件的情况下返回一个新列表,如果您进行以下更改:

implicit class FilterHelper[A](l: List[A]) {
  def ifthen[B](cond: Boolean, f:(List[A]) => List[B]) = {
    if (cond) f(l) else l
  }
}

然后像这样使用它:

val list = List("1", "2")    
val l2 = list.ifthen(someCondition, _.filter(f1)
val l3 = list.ifthen(someOtherCondition, _.map(_.size))
于 2013-07-01T14:50:19.133 回答
2

将条件包含在过滤器的块中会相当简单,如下所示:

val result = strs filter (x => !cond1 || f1(x)) filter (x => !cond2 || f2(x))

如果满足条件,结果将应用过滤器,或者简单地返回相同的列表。

于 2013-07-01T14:55:53.423 回答