如何跳出 Scala 中的循环?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2742719/
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 do I break out of a loop in Scala?
提问by TiansHUo
How do I break out a loop?
我如何打破一个循环?
var largest=0
for(i<-999 to 1 by -1) {
for (j<-i to 1 by -1) {
val product=i*j
if (largest>product)
// I want to break out here
else
if(product.toString.equals(product.toString.reverse))
largest=largest max product
}
}
How do I turn nested for loops into tail recursion?
如何将嵌套的 for 循环转换为尾递归?
From Scala Talk at FOSDEM 2009 http://www.slideshare.net/Odersky/fosdem-2009-1013261on the 22nd page:
来自 2009 年 FOSDEM 的 Scala 谈话http://www.slideshare.net/Odersky/fosdem-2009-1013261第 22 页:
Break and continue Scala does not have them. Why? They are a bit imperative; better use many smaller functions Issue how to interact with closures. They are not needed!
中断并继续 Scala 没有它们。为什么?它们有点势在必行;更好地使用许多较小的函数 问题如何与闭包交互。他们不需要!
What is the explanation?
解释是什么?
回答by Rex Kerr
You have three (or so) options to break out of loops.
您有三个(左右)选项可以跳出循环。
Suppose you want to sum numbers until the total is greater than 1000. You try
假设您想对数字求和,直到总数大于 1000。您尝试
var sum = 0
for (i <- 0 to 1000) sum += i
except you want to stop when (sum > 1000).
除非您想在 (sum > 1000) 时停止。
What to do? There are several options.
该怎么办?有几种选择。
(1a) Use some construct that includes a conditional that you test.
(1a) 使用一些包含您测试的条件的结构。
var sum = 0
(0 to 1000).iterator.takeWhile(_ => sum < 1000).foreach(i => sum+=i)
(warning--this depends on details of how the takeWhile test and the foreach are interleaved during evaluation, and probably shouldn't be used in practice!).
(警告——这取决于 takeWhile 测试和 foreach 在评估期间如何交错的细节,可能不应该在实践中使用!)。
(1b) Use tail recursion instead of a for loop, taking advantage of how easy it is to write a new method in Scala:
(1b) 使用尾递归而不是 for 循环,利用在 Scala 中编写新方法是多么容易:
var sum = 0
def addTo(i: Int, max: Int) {
sum += i; if (sum < max) addTo(i+1,max)
}
addTo(0,1000)
(1c) Fall back to using a while loop
(1c) 回退到使用 while 循环
var sum = 0
var i = 0
while (i <= 1000 && sum <= 1000) { sum += 1; i += 1 }
(2) Throw an exception.
(2) 抛出异常。
object AllDone extends Exception { }
var sum = 0
try {
for (i <- 0 to 1000) { sum += i; if (sum>=1000) throw AllDone }
} catch {
case AllDone =>
}
(2a) In Scala 2.8+ this is already pre-packaged in scala.util.control.Breaksusing syntax that looks a lot like your familiar old break from C/Java:
(2a) 在 Scala 2.8+ 中,这已经预先打包在scala.util.control.Breaksusing 语法中,看起来很像您熟悉的 C/Java 旧突破:
import scala.util.control.Breaks._
var sum = 0
breakable { for (i <- 0 to 1000) {
sum += i
if (sum >= 1000) break
} }
(3) Put the code into a method and use return.
(3)将代码放入方法中,使用return。
var sum = 0
def findSum { for (i <- 0 to 1000) { sum += i; if (sum>=1000) return } }
findSum
This is intentionally made not-too-easy for at least three reasons I can think of. First, in large code blocks, it's easy to overlook "continue" and "break" statements, or to think you're breaking out of more or less than you really are, or to need to break two loops which you can't do easily anyway--so the standard usage, while handy, has its problems, and thus you should try to structure your code a different way. Second, Scala has all sorts of nestings that you probably don't even notice, so if you could break out of things, you'd probably be surprised by where the code flow ended up (especially with closures). Third, most of Scala's "loops" aren't actually normal loops--they're method calls that have their own loop, or they are recursion which may or may not actually be a loop--and although they actlooplike, it's hard to come up with a consistent way to know what "break" and the like should do. So, to be consistent, the wiser thing to do is not to have a "break" at all.
出于我能想到的至少三个原因,故意使这不太容易。首先,在大型代码块中,很容易忽略“continue”和“break”语句,或者认为您的中断比实际情况多或少,或者需要中断两个您无法做到的循环无论如何都很容易——所以标准用法虽然方便,但也有问题,因此您应该尝试以不同的方式构建代码。其次,Scala 有各种各样的嵌套,您可能甚至都没有注意到,所以如果您能打破一些东西,您可能会对代码流的结束位置(尤其是闭包)感到惊讶。第三,Scala 的大部分“循环”实际上并不是普通的循环——它们是有自己循环的方法调用,looplike,很难想出一个一致的方法来知道“break”等应该做什么。因此,要保持一致,更明智的做法是根本不要“休息”。
Note: There are functional equivalents of all of these where you return the value of sumrather than mutate it in place. These are more idiomatic Scala. However, the logic remains the same. (returnbecomes return x, etc.).
注意:所有这些都有等价的功能,您可以在其中返回值sum而不是原地变异。这些是更惯用的 Scala。但是,逻辑保持不变。(return变成return x,等等)。
回答by hohonuuli
This has changed in Scala 2.8 which has a mechanism for using breaks. You can now do the following:
这在 Scala 2.8 中发生了变化,它具有使用中断的机制。您现在可以执行以下操作:
import scala.util.control.Breaks._
var largest = 0
// pass a function to the breakable method
breakable {
for (i<-999 to 1 by -1; j <- i to 1 by -1) {
val product = i * j
if (largest > product) {
break // BREAK!!
}
else if (product.toString.equals(product.toString.reverse)) {
largest = largest max product
}
}
}
回答by Keith Blanchard
It is never a good idea to break out of a for-loop. If you are using a for-loop it means that you know how many times you want to iterate. Use a while-loop with 2 conditions.
跳出 for 循环从来都不是一个好主意。如果您使用的是 for 循环,则意味着您知道要迭代多少次。使用具有 2 个条件的 while 循环。
for example
例如
var done = false
while (i <= length && !done) {
if (sum > 1000) {
done = true
}
}
回答by Patrick
To add Rex Kerr answer another way:
要以另一种方式添加 Rex Kerr 答案:
(1c) You can also use a guard in your loop:
var sum = 0 for (i <- 0 to 1000 ; if sum<1000) sum += i
(1c) 您还可以在循环中使用警卫:
var sum = 0 for (i <- 0 to 1000 ; if sum<1000) sum += i
回答by Ham Vocke
Since there is no breakin Scala yet, you could try to solve this problem with using a return-statement. Therefore you need to put your inner loop into a function, otherwise the return would skip the whole loop.
由于breakScala 中还没有,您可以尝试使用return-statement来解决此问题。因此,您需要将内部循环放入一个函数中,否则返回将跳过整个循环。
Scala 2.8 however includes a way to break
然而,Scala 2.8 包含了一种破解方法
http://www.scala-lang.org/api/rc/scala/util/control/Breaks.html
http://www.scala-lang.org/api/rc/scala/util/control/Breaks.html
回答by user1836270
// import following package
import scala.util.control._
// create a Breaks object as follows
val loop = new Breaks;
// Keep the loop inside breakable as follows
loop.breakable{
// Loop will go here
for(...){
....
// Break will go here
loop.break;
}
}
use Break module http://www.tutorialspoint.com/scala/scala_break_statement.htm
使用中断模块 http://www.tutorialspoint.com/scala/scala_break_statement.htm
回答by pathikrit
Just use a while loop:
只需使用一个while循环:
var (i, sum) = (0, 0)
while (sum < 1000) {
sum += i
i += 1
}
回答by elm
An approach that generates the values over a range as we iterate, up to a breaking condition, instead of generating first a whole range and then iterating over it, using Iterator, (inspired in @RexKerr use of Stream)
一种在我们迭代时在一个范围内生成值的方法,直到破坏条件,而不是首先生成整个范围,然后使用Iterator, (灵感来自 @RexKerr 使用Stream)
var sum = 0
for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i
回答by fresskoma
Here is a tail recursive version. Compared to the for-comprehensions it is a bit cryptic, admittedly, but I'd say its functional :)
这是一个尾递归版本。与 for-comprehensions 相比,无可否认,它有点神秘,但我会说它的功能:)
def run(start:Int) = {
@tailrec
def tr(i:Int, largest:Int):Int = tr1(i, i, largest) match {
case x if i > 1 => tr(i-1, x)
case _ => largest
}
@tailrec
def tr1(i:Int,j:Int, largest:Int):Int = i*j match {
case x if x < largest || j < 2 => largest
case x if x.toString.equals(x.toString.reverse) => tr1(i, j-1, x)
case _ => tr1(i, j-1, largest)
}
tr(start, 0)
}
As you can see, the tr function is the counterpart of the outer for-comprehensions, and tr1 of the inner one. You're welcome if you know a way to optimize my version.
如您所见,tr 函数是外部 for-comprehensions 的对应函数,而 tr1 是内部 for-comprehensions 的对应函数。如果您知道优化我的版本的方法,欢迎您。
回答by user unknown
Close to your solution would be this:
接近您的解决方案是这样的:
var largest = 0
for (i <- 999 to 1 by -1;
j <- i to 1 by -1;
product = i * j;
if (largest <= product && product.toString.reverse.equals (product.toString.reverse.reverse)))
largest = product
println (largest)
The j-iteration is made without a new scope, and the product-generation as well as the condition are done in the for-statement (not a good expression - I don't find a better one). The condition is reversed which is pretty fast for that problem size - maybe you gain something with a break for larger loops.
j 迭代是在没有新范围的情况下进行的,产品生成和条件在 for 语句中完成(不是一个好的表达 - 我没有找到更好的表达)。条件被反转,这对于该问题的大小来说非常快 - 也许您可以通过中断更大的循环获得一些东西。
String.reverse implicitly converts to RichString, which is why I do 2 extra reverses. :) A more mathematical approach might be more elegant.
String.reverse 隐式转换为 RichString,这就是我执行 2 个额外反转的原因。:) 更数学化的方法可能更优雅。

