以下命令性代码的最有效功能版本是什么?

时间:2010-12-24 02:13:06

标签: scala functional-programming

我正在学习Scala,我想知道使用Scala的函数式编程功能表达这种命令式模式的最佳方式。

def f(l: List[Int]): Boolean = {
  for (e <- l) {
    if (test(e))
      return true
    }
  }
  return false
}

我能想到的最好的是:

l map { e => test(e) } contains true

但是效率较低,因为它在每个元素上调用test(),而命令式版本在第一个满足test()的元素上停止。是否有更惯用的函数式编程技术可以用于同样的效果?在Scala中,命令式版本看起来很尴尬。

3 个答案:

答案 0 :(得分:17)

您可以使用exists方法:

val listWithEvens = List(1,2,3,4)
val listWithoutEvens = List(1,3,5)
def test(e: Int) = e % 2 == 0

listWithEvens.exists(test(_)) // true
listWithoutEvens.exists(test(_)) // false

// alternative
listWithEvens.exists(_ % 2 == 0)  // true 

如果您不熟悉_这样使用,它相当于:

listWithEvens.exists(v => v % 2 == 0)

答案 1 :(得分:7)

所以,你想要的是exists方法(l.exists(test)),它没有说明你如何实现它。最简单的实现效率不高:

def f(l: List[Int]): Boolean = l.foldLeft(false)((flag, n) => flag || test(n))

问题在于它会遍历所有l,即使测试在flag变为真时停止。现在,大多数迭代的函数方法(在严格的语言中)都不会停止迭代直到完成所有集合。那些实际上实现的就像你一样,所以,最后,你只是隐藏那种代码,而不是避免它。

但是,如果我被要求使用现有方法,当然,不使用exists,并且在此基础上效率更高,可以这样做:

def f(l: List[Int]): Boolean = l.dropWhile(!test(_)).nonEmpty

答案 2 :(得分:1)

当然在这种情况下没有必要(我实际上希望Scala库为他们自己的exists使用更强制性的版本 - 或者至少有一个map带有显式{ {1}}但是在break上你可以使用一个简单的(尾部)递归函数。

List

由于import scala.annotation.tailrec @tailrec def exists(l: List[Int], p: (Int) => Boolean): Boolean = l match { case Nil => false case x :: xs => p(x) || exists(xs, p) } 仅评估右侧,如果左侧为||,则表示您提前休息。

当然,如果引用集合的尾部很便宜,这只是有效的。