我如何打破一个循环?

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没有它们。为什么? 它们有点迫不得已;最好使用许多较小的函数 发布如何与闭包交互。 他们是不需要的!

怎么解释呢?


当前回答

我遇到了类似下面代码的情况

 for(id<-0 to 99) {
    try {
      var symbol = ctx.read("$.stocks[" + id + "].symbol").toString
      var name = ctx.read("$.stocks[" + id + "].name").toString
      stocklist(symbol) = name
    }catch {
      case ex: com.jayway.jsonpath.PathNotFoundException=>{break}
    }
  }

我正在使用一个java库和机制是ctx。read在找不到任何东西时抛出异常。 我被困在这样的情况中:当抛出异常时,我必须打破循环,但是scala.util.control. break .break使用异常来打破循环,并且它在catch块中,因此它被捕获。

我有一个丑陋的方法来解决这个问题:第一次执行循环,并获得实际长度的计数。 然后用它来做第二个循环。

当你使用一些java库时,从Scala中解脱出来并不是那么好。

其他回答

这是一个尾部递归的版本。不可否认,与for推导式相比,它有点晦涩,但我认为它是功能性的:)

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

如您所见,tr函数是外部for推导式的对应函数,tr1是内部for推导式的对应函数。如果你知道如何优化我的版本,欢迎你。

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

使用Break模块 http://www.tutorialspoint.com/scala/scala_break_statement.htm

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

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

我不知道Scala风格在过去的9年里发生了多大的变化,但我发现一个有趣的现象:大多数现有的答案都使用了var,或者是难以阅读的递归。尽早退出的关键是使用惰性集合生成可能的候选对象,然后分别检查条件。生成产物:

val products = for {
  i <- (999 to 1 by -1).view
  j <- (i to 1 by -1).view
} yield (i*j)

然后在不生成所有组合的情况下从视图中找到第一个回文:

val palindromes = products filter {p => p.toString == p.toString.reverse}
palindromes.head

要找到最大的回文(尽管懒惰不会给你带来什么好处,因为你必须检查整个列表):

palindromes.max

您的原始代码实际上是在检查第一个大于后续产品的回文,这与检查第一个回文是一样的,只是在一个奇怪的边界条件下,我认为这不是您想要的。乘积不是严格单调递减的。例如,998*998大于999*997,但在循环中出现得更晚。

不管怎样,分离的惰性生成和条件检查的优点是,你写它的时候就像使用整个列表一样,但它只生成你需要的东西。你可以说是两全其美了。

在scala中我们能做的就是

scala> import util.control.Breaks._

scala> object TestBreak {
       def main(args : Array[String]) {
         breakable {
           for (i <- 1 to 10) {
             println(i)
             if (i == 5)
               break;
       } } } }

输出:

scala> TestBreak.main(Array())
1
2
3
4
5