scala 如何从scala中的for循环产生单个元素?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13343531/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to yield a single element from for loop in scala?
提问by Julio Faerman
Much like this question:
很像这个问题:
Functional code for looping with early exit
Say the code is
说代码是
def findFirst[T](objects: List[T]):T = {
for (obj <- objects) {
if (expensiveFunc(obj) != null) return /*???*/ Some(obj)
}
None
}
How to yield a single element from a for loop like this in scala?
如何在scala中从这样的for循环中产生单个元素?
I do not want to use find, as proposed in the original question, i am curious about if and how it could be implemented using the for loop.
我不想使用原始问题中提出的 find ,我很好奇它是否以及如何使用 for 循环来实现。
* UPDATE *
* 更新 *
First, thanks for all the comments, but i guess i was not clear in the question. I am shooting for something like this:
首先,感谢所有评论,但我想我在问题中不清楚。我正在拍摄这样的东西:
val seven = for {
x <- 1 to 10
if x == 7
} return x
And that does not compile. The two errors are: - return outside method definition - method main has return statement; needs result type
这不会编译。这两个错误是: - 在方法定义之外返回 - 方法 main 有 return 语句;需要结果类型
I know find() would be better in this case, i am just learning and exploring the language. And in a more complex case with several iterators, i think finding with for can actually be usefull.
我知道 find() 在这种情况下会更好,我只是在学习和探索这门语言。在具有多个迭代器的更复杂的情况下,我认为使用 for 查找实际上很有用。
Thanks commenters, i'll start a bounty to make up for the bad posing of the question :)
感谢评论者,我将开始悬赏以弥补问题的不良姿势:)
采纳答案by dapek
You can turn your list into a stream, so that any filters that the for-loop contains are only evaluated on-demand. However, yielding from the stream will always return a stream, and what you want is I suppose an option, so, as a final step you can check whether the resulting stream has at least one element, and return its head as a option. The headOption function does exactly that.
您可以将列表转换为流,以便仅按需评估 for 循环包含的任何过滤器。但是,从流中产生总是会返回一个流,而您想要的是我假设一个选项,因此,作为最后一步,您可以检查生成的流是否至少有一个元素,并将其头部作为选项返回。headOption 函数正是这样做的。
def findFirst[T](objects: List[T], expensiveFunc: T => Boolean): Option[T] =
(for (obj <- objects.toStream if expensiveFunc(obj)) yield obj).headOption
回答by Philippe
If you want to use a forloop, which uses a nicer syntax than chained invocations of .find, .filter, etc., there is a neat trick. Instead of iterating over strict collections like list, iterate over lazy ones like iterators or streams. If you're starting with a strict collection, make it lazy with, e.g. .toIterator.
如果你想使用一个for回路,它采用了更好的语法比链式调用.find,.filter等等,有一个巧妙的花招。不是迭代像列表这样的严格集合,而是迭代像迭代器或流这样的惰性集合。如果你从一个严格的集合开始,让它变得懒惰,例如.toIterator。
Let's see an example.
让我们看一个例子。
First let's define a "noisy" int, that will show us when it is invoked
首先让我们定义一个“noisy”int,它会告诉我们它何时被调用
def noisyInt(i : Int) = () => { println("Getting %d!".format(i)); i }
Now let's fill a list with some of these:
现在让我们用其中的一些填充一个列表:
val l = List(1, 2, 3, 4).map(noisyInt)
We want to look for the first element which is even.
我们要寻找第一个偶数元素。
val r1 = for(e <- l; val v = e() ; if v % 2 == 0) yield v
The above line results in:
上面的行导致:
Getting 1!
Getting 2!
Getting 3!
Getting 4!
r1: List[Int] = List(2, 4)
...meaning that all elements were accessed. That makes sense, given that the resulting list contains alleven numbers. Let's iterate over an iterator this time:
...意味着所有元素都被访问。这是有道理的,因为结果列表包含所有偶数。这次让我们迭代一个迭代器:
val r2 = (for(e <- l.toIterator; val v = e() ; if v % 2 == 0) yield v)
This results in:
这导致:
Getting 1!
Getting 2!
r2: Iterator[Int] = non-empty iterator
Notice that the loop was executed only up to the point were it could figure out whether the result was an empty or non-empty iterator.
请注意,循环只执行到它可以确定结果是空迭代器还是非空迭代器为止。
To get the first result, you can now simply call r2.next.
要获得第一个结果,您现在只需调用r2.next.
If you want a result of an Optiontype, use:
如果你想要一个Option类型的结果,请使用:
if(r2.hasNext) Some(r2.next) else None
EditYour second example in this encoding is just:
编辑此编码中的第二个示例只是:
val seven = (for {
x <- (1 to 10).toIterator
if x == 7
} yield x).next
...of course, you should be sure that there is always at least a solution if you're going to use .next. Alternatively, use headOption, defined for all Traversables, to get an Option[Int].
...当然,如果您要使用.next. 或者,使用headOption为所有Traversables定义的 来获得Option[Int]。
回答by Malte Schwerhoff
Why not do exactly what you sketched above, that is, returnfrom the loop early? If you are interested in what Scala actually does under the hood, run your code with -print. Scala desugares the loop into a foreachand then uses an exception to leave the foreachprematurely.
为什么不完全按照上面勾画的方式进行,即return尽早从循环开始?如果您对 Scala 在幕后实际执行的操作感兴趣,请使用-print. Scala 将循环脱糖为 a foreach,然后使用异常提前离开foreach。
回答by Udayakumar Rayala
So what you are trying to do is to break out a loop after your condition is satisfied. Answer here might be what you are looking for. How do I break out of a loop in Scala?.
因此,您要做的是在满足条件后打破循环。这里的答案可能就是您正在寻找的。如何跳出 Scala 中的循环?.
Overall, for comprehension in Scala is translated into map, flatmap and filter operations. So it will not be possible to break out of these functions unless you throw an exception.
总的来说,for comprehension 在 Scala 中被翻译成 map、flatmap 和 filter 操作。因此,除非抛出异常,否则无法跳出这些函数。
回答by Faruk Sahin
If you are wondering, this is how find is implemented in LineerSeqOptimized.scala; which List inherits
如果您想知道,这就是在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
}
回答by Andreas Neumann
This is a horrible hack. But it would get you the result you wished for.
这是一个可怕的黑客。但它会让你得到你想要的结果。
Idiomatically you'd use a Stream or View and just compute the parts you need.
习惯上,您将使用 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
}
}
}
回答by Vincenzo Maggio
Why not something like
为什么不像
object Main {
def main(args: Array[String]): Unit = {
val seven = (for (
x <- 1 to 10
if x == 7
) yield x).headOption
}
}
Variable sevenwill be an Option holding Some(value)if value satisfies condition
如果值满足条件,变量seven将是一个 Option 持有Some(value)
回答by J Camphor
I hope to help you.
我希望能帮助你。
I think ... no 'return' impl.
我认为......没有'回报' impl。
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
}
}
回答by Jürgen Strobel
objects iterator filter { obj => (expensiveFunc(obj) != null } next
The trick is to get some lazy evaluated view on the colelction, either an iterator or a Stream, or objects.view. The filter will only execute as far as needed.
诀窍是在集合上获得一些懒惰的评估视图,可以是迭代器或流,也可以是 objects.view。过滤器只会在需要时执行。

