我正在学习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中,命令式版本看起来很尴尬。
答案 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)
}
仅评估右侧,如果左侧为||
,则表示您提前休息。
当然,如果引用集合的尾部很便宜,这只是有效的。