我如何打破一个循环?

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
    }
}

如何将嵌套for循环转换为尾递归?

来自FOSDEM 2009 Scala Talk http://www.slideshare.net/Odersky/fosdem-2009-1013261 第22页:

中断并继续 Scala没有它们。为什么? 它们有点迫不得已;最好使用许多较小的函数 发布如何与闭包交互。 他们是不需要的!

怎么解释呢?


当前回答

巧妙地使用find方法进行收集将为您提供帮助。

var largest = 0
lazy val ij =
  for (i <- 999 to 1 by -1; j <- i to 1 by -1) yield (i, j)

val largest_ij = ij.find { case(i,j) =>
  val product = i * j
  if (product.toString == product.toString.reverse)
    largest = largest max product
  largest > product
}

println(largest_ij.get)
println(largest)

其他回答

只需使用while循环:

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}

由于Scala中还没有break,您可以尝试使用return语句来解决这个问题。因此,您需要将内部循环放入函数中,否则返回将跳过整个循环。

然而,Scala 2.8包含了一种打破的方法

http://www.scala-lang.org/api/rc/scala/util/control/Breaks.html

您有三个(大约)选项可以跳出循环。

假设您希望将数字相加,直到总数大于1000。你尝试

var sum = 0
for (i <- 0 to 1000) sum += i

除非你想在(sum > 1000)时停止。

怎么办呢?有几种选择。

(1a)使用一些包含你要测试的条件的结构。

var sum = 0
(0 to 1000).iterator.takeWhile(_ => sum < 1000).foreach(i => sum+=i)

(警告——这取决于takeWhile测试和foreach在评估期间如何交织的细节,并且可能不应该在实践中使用!)

(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)回到使用while循环

var sum = 0
var i = 0
while (i <= 1000 && sum <= 1000) { sum += 1; i += 1 }

(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)在Scala 2.8+中,这已经被预先打包在Scala .util.control. breaks中,使用的语法看起来很像你熟悉的C/Java break:

import scala.util.control.Breaks._
var sum = 0
breakable { for (i <- 0 to 1000) {
  sum += i
  if (sum >= 1000) break
} }

(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 act looplike, 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.

注意:在返回sum的值而不是原地改变它的地方,所有这些函数都有等价的功能。这些是更习惯的Scala。然而,逻辑是一样的。(return变成return x,等等)。

再加上雷克斯·科尔的另一种回答:

(1c)你也可以在你的循环中使用守卫: Var和= 0 For (i <- 0到1000;如果sum<1000) sum += I

接近你的解决方案是这样的:

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)

j迭代是在没有新的作用域的情况下进行的,产品生成和条件都是在for语句中完成的(这不是一个好的表达式-我没有找到更好的表达式)。对于这个问题大小来说,条件是反向的,这是相当快的——也许对于更大的循环,您可以通过中断获得一些东西。

字符串。reverse隐式转换为RichString,这就是为什么我做了2个额外的反转。:)一个更数学的方法可能会更优雅。