20

很像这个问题:

提前退出循环的功能代码

说代码是

def findFirst[T](objects: List[T]):T = {
  for (obj <- objects) {
    if (expensiveFunc(obj) != null) return /*???*/ Some(obj)
  }
  None
}

如何在scala中从这样的for循环中产生单个元素?

我不想使用 find,正如原始问题中提出的那样,我很好奇是否以及如何使用 for 循环来实现它。

* 更新 *

首先,感谢所有评论,但我想我的问题并不清楚。我正在拍摄这样的东西:

val seven = for {
    x <- 1 to 10
    if x == 7
} return x

这不会编译。这两个错误是: - 在方法定义之外返回 - 方法 main 有返回语句;需要结果类型

我知道 find() 在这种情况下会更好,我只是在学习和探索语言。在具有多个迭代器的更复杂的情况下,我认为使用 for 查找实际上是有用的。

感谢评论者,我会开始赏金来弥补这个问题的糟糕姿势:)

4

9 回答 9

25

如果你想使用一个循环,它使用比,等for的链式调用更好的语法,有一个巧妙的技巧。与其迭代像列表这样的严格集合,不如迭代像迭代器或流这样的惰性集合。如果你从一个严格的集合开始,让它变得懒惰,例如..find.filter.toIterator

让我们看一个例子。

首先让我们定义一个“嘈杂”的 int,它会在它被调用时显示给我们

def noisyInt(i : Int) = () => { println("Getting %d!".format(i)); i }

现在让我们用其中的一些来填写一个列表:

val l = List(1, 2, 3, 4).map(noisyInt)

我们要寻找第一个偶数元素。

val r1 = for(e <- l; val v = e() ; if v % 2 == 0) yield v

上面的行导致:

Getting 1!
Getting 2!
Getting 3!
Getting 4!
r1: List[Int] = List(2, 4)

...意味着访问了所有元素。这是有道理的,因为结果列表包含所有偶数。这次让我们迭代一个迭代器:

val r2 = (for(e <- l.toIterator; val v = e() ; if v % 2 == 0) yield v)

这导致:

Getting 1!
Getting 2!
r2: Iterator[Int] = non-empty iterator

请注意,循环只执行到它可以确定结果是空迭代器还是非空迭代器。

要获得第一个结果,您现在只需调用r2.next.

如果你想要一个Option类型的结果,使用:

if(r2.hasNext) Some(r2.next) else None

编辑您在此编码中的第二个示例只是:

val seven = (for {
    x <- (1 to 10).toIterator
    if x == 7
} yield x).next

...当然,如果您要使用.next. 或者,使用headOption为所有Traversables 定义的 来获得Option[Int].

于 2012-11-12T12:49:13.227 回答
18

您可以将列表转换为流,以便 for 循环包含的任何过滤器仅按需评估。但是,从流中产生将始终返回一个流,而您想要的是我想一个选项,因此,作为最后一步,您可以检查生成的流是否具有至少一个元素,并将其头部作为选项返回。headOption 函数正是这样做的。

def findFirst[T](objects: List[T], expensiveFunc: T => Boolean): Option[T] =
    (for (obj <- objects.toStream if expensiveFunc(obj)) yield obj).headOption
于 2012-11-16T15:19:30.597 回答
3

为什么不完全按照您上面的草图进行操作,即return从循环早期开始?如果您对 Scala 在后台实际执行的操作感兴趣,请使用-print. Scala将循环脱糖成a foreach,然后使用异常提前离开foreach

于 2012-11-12T12:32:48.583 回答
2

所以你要做的是在你的条件得到满足后打破一个循环。这里的答案可能是您正在寻找的。如何在 Scala 中跳出循环?.

总的来说,Scala 中的 for comprehension 被翻译为 map、flatmap 和 filter 操作。因此,除非您抛出异常,否则无法突破这些功能。

于 2012-11-12T12:32:33.080 回答
1

如果您想知道,这就是在LineerSeqOptimized.scala中实现 find 的方式;List 继承了哪个

override /*IterableLike*/
  def find(p: A => Boolean): Option[A] = {
    var these = this
    while (!these.isEmpty) {
      if (p(these.head)) return Some(these.head)
      these = these.tail
    }
    None
  }
于 2012-11-12T12:44:49.073 回答
1

这是一个可怕的黑客。但它会让你得到你想要的结果。

习惯上你会使用 Stream 或 View 并且只计算你需要的部分。

def findFirst[T](objects: List[T]): T = {

def expensiveFunc(o : T)  = // unclear what should be returned here

case class MissusedException(val data: T) extends Exception

try {
  (for (obj <- objects) {
    if (expensiveFunc(obj) != null) throw new MissusedException(obj)
  })
  objects.head // T must be returned from loop, dummy
} catch {
  case MissusedException(obj) => obj
}

}

于 2012-11-12T12:54:54.370 回答
1

为什么不喜欢

object Main {     
  def main(args: Array[String]): Unit = {
    val seven = (for (
    x <- 1 to 10
    if x == 7
    ) yield x).headOption
  }
}

如果值满足条件,变量seven将是持有的期权Some(value)

于 2012-11-19T13:16:38.617 回答
0

我希望能帮助你。

我认为......没有“回报”的暗示。

object TakeWhileLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else
        seq(seq.takeWhile(_ == null).size)
}

object OptionLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T], index: Int = 0): T = if (seq.isEmpty) null.asInstanceOf[T] else
        Option(seq(index)) getOrElse func(seq, index + 1)
}

object WhileLoop extends App {
    println("first non-null: " + func(Seq(null, null, "x", "y", "z")))

    def func[T](seq: Seq[T]): T = if (seq.isEmpty) null.asInstanceOf[T] else {
        var i = 0
        def obj = seq(i)
        while (obj == null)
            i += 1
        obj
    }
}
于 2012-11-12T16:19:29.470 回答
0
objects iterator filter { obj => (expensiveFunc(obj) != null } next

诀窍是在集合上获得一些惰性评估视图,可以是迭代器或流,或 objects.view。过滤器只会在需要时执行。

于 2012-11-15T16:30:44.357 回答