如何从scala中的for循环中生成单个元素?

时间:2012-11-12 12:13:41

标签: scala

很像这个问题:

Functional code for looping with early exit

说代码是

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有return语句;需要结果类型

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

感谢评论者,我会开始赏金以弥补问题的不良后果:)

9 个答案:

答案 0 :(得分:21)

如果你想使用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定义的Traversable来获取Option[Int]

答案 1 :(得分:18)

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

def findFirst[T](objects: List[T], expensiveFunc: T => Boolean): Option[T] =
    (for (obj <- objects.toStream if expensiveFunc(obj)) yield obj).headOption

答案 2 :(得分:3)

为什么不尽早完成你在上面描绘的内容,即早期的return?如果您对Scala实际上做了什么感兴趣,请使用-print运行您的代码。 Scala将循环变为foreach,然后使用异常提前离开foreach

答案 3 :(得分:2)

所以你要做的就是在条件满足后打破一个循环。这里的答案可能就是你要找的东西。 How do I break out of a loop in Scala?

总的来说,Scala中的理解被翻译成map,flatmap和filter操作。因此除非你抛出异常,否则不可能打破这些函数。

答案 4 :(得分:1)

如果您想知道,这就是LineerSeqOptimized.scala中实现查找的方式;哪个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
  }

答案 5 :(得分:1)

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

通过惯用法,您可以使用流或视图来计算所需的部分。

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
}

}

答案 6 :(得分: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)的选项

答案 7 :(得分: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
    }
}

答案 8 :(得分:0)

objects iterator filter { obj => (expensiveFunc(obj) != null } next

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