简体   繁体   English

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

[英]What is the most efficient functional version of the following imperative code?

I'm learning Scala and I want to know the best way of expressing this imperative pattern using Scala's functional programming capabilities. 我正在学习Scala,我想知道使用Scala的函数编程功能表达这种命令式模式的最佳方式。

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

The best I can come up with is along the lines of: 我能想到的最好的是:

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

But this is less efficient since it calls test() on each element, whereas the imperative version stops on the first element that satisfies test(). 但是效率较低,因为它在每个元素上调用test(),而命令式版本在第一个满足test()的元素上停止。 Is there a more idiomatic functional programming technique I can use to the same effect? 是否有更惯用的函数式编程技术可以用于同样的效果? The imperative version seems awkward in Scala. 在Scala中,命令式版本似乎很尴尬。

You can use the exists method: 您可以使用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 

If you are not familiar with the _ used like this, it's the equivalent of: 如果您不熟悉_这样使用,它相当于:

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

So, what you want is the exists method ( l.exists(test) ), which says nothing of how you'd implement it. 那么,你想要的是exists方法( l.exists(test) ),它没有说明你如何实现它。 The simplest implementation isn't very efficient: 最简单的实现效率不高:

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

The problem is that it will iterate through all of l , even if the testing stops once flag becomes true. 问题是它将遍历所有l ,即使测试在flag变为真时停止。 Now, most functional methods of iteration (in strict languages) will not stop iterating until finished through all the collection. 现在,大多数迭代的函数方法(在严格的语言中)都不会停止迭代直到完成所有集合。 The ones that do are actually implemented pretty much like you did, so, in the end, you are just hiding that sort of code, not avoiding it. 那些实际上实现的就像你一样,所以,最后,你只是隐藏那种代码,而不是避免它。

However, if I were required to use existing methods, and, of course, not to use exists , and be efficient on top of that, one could do something like this: 但是,如果我被要求使用现有方法,当然,不使用exists ,并且在此基础上有效,可以做类似这样的事情:

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

Of course it isn't necessary in this case (and I actually expect the Scala library to use a more imperative version for their own exists – or at least have a map with an explicit break in it) but on a List you can use a simple (tail) recursive function. 当然,在这种情况下没有必要(我实际上期望Scala库为他们自己的exists使用更强制性的版本 - 或者至少有一个带有显式breakmap )但是在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)
}

Because || 因为|| only evaluates the right side if the left side is false it means you break early. 如果左侧为false则仅评估右侧,这意味着您提前休息。

This, of course, is only efficient if referencing the tail of a collection is cheap. 当然,这只有在引用集合的尾部便宜时才有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM