我如何摆脱斯卡拉循环?

我如何打破循环?

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

如何将嵌套循环转换为尾recursion?

从FPSDEM 2009的Scala讲座http://www.slideshare.net/Odersky/fosdem-2009-1013261在第22页:

打破并继续斯卡拉没有他们。 为什么? 他们有点必要; 更好地使用许多较小的函数问题如何与闭包进行交互。 他们不需要!

什么是解释?

你有三个(或多个)选项来打破循环。

假设你想总和数字,直到总数大于1000.你试试

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

除非你想停止(总和> 1000)。

该怎么办? 有几个选项。

(1a)使用一些包含您testing的条件的构造。

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

(警告 – 这取决于在评估期间takeWhiletesting和foreach是如何交错的细节,并且可能不应该在实践中使用!)。

(1b)使用尾recursion而不是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)抛出exception。

 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的旧版本:

 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 

至less有三个我能想到的理由,故意让这个过程变得不太容易。 首先,在大的代码块中,很容易忽略“继续”和“中断”语句,或者认为自己比真实的要多或less,或者需要打破两个你不能做的循环很容易 – 所以标准的使用,虽然方便,有其问题,因此你应该尝试以不同的方式来构build你的代码。 其次,斯卡拉有各种各样的嵌套,你可能根本没有注意到,所以如果你能摆脱这些事情,你可能会惊讶于代码stream的结束(特别是closures)。 第三,Scala的大部分“循环”实际上并不是正常的循环 – 它们是方法调用,它们有自己的循环,或者它们是recursion,可能实际上也可能不是循环 – 虽然它们循环操作,但很难想出一个一致的方法来知道什么“突破”之类的应该做的。 所以,为了保持一致,更明智的做法是不要有“rest”。

注意 :所有这些函数等价物都是返回sum的值,而不是将其改变。 这些更加习惯斯卡拉。 但是,逻辑保持不变。 ( return成为return x等)。

这已经改变了Scala 2.8,它有一个使用rest的机制。 您现在可以执行以下操作:

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

打破for循环从来就不是一个好主意。 如果你正在使用for循环,这意味着你知道你要迭代多less次。 使用2个条件的while循环。

例如

 var done = false while (i <= length && !done) { if (i > 1000) { done = true; } 

要添加雷克斯克尔回答另一种方式:

  • (1c)你也可以在你的循环中使用一个警卫:

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

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

然而,Scala 2.8包含了一个破解的方法

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

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

只需使用while循环:

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

一种方法,在我们迭代时产生一个范围内的值,直到一个破坏条件,而不是首先产生一个整个范围,然后使用Iterator (在@RexKerr中使用Stream

 var sum = 0 for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i 

接近你的解决scheme将是这样的:

 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语句中完成(不是一个好的expression式 – 我没有find更好的expression式)。 条件逆转,对于这个问题的规模相当快 – 也许你获得了一个更大的循环rest的东西。

String.reverse隐式转换为RichString,这就是为什么我做了2个额外的反转。 :)更math的方法可能会更优雅。

这是一个尾recursion版本。 诚然,相比于理解这是有点神秘,但我会说它的function:)

 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函数是外部理解的内容,tr1是内部理解的一部分。 如果您知道优化我的版本的方法,欢迎您。

具有讽刺意味的是scala.util.control.Breaks的Scala中断是一个例外:

 def break(): Nothing = { throw breakException } 

最好的build议是:不要使用break,continue和goto! 国际海事组织是一样的,不好的做法和各种问题(和热门讨论)的邪恶来源,最后“被认为是有害的”。 代码块结构化,在这个例子中也是多余的。 我们的Edsger W. Dijkstra†写道:

程序员的素质是他们产生的程序中语句密度的递减函数。

第三方breakable包是一种可能的select

https://github.com/erikerlandson/breakable

示例代码:

 scala> import com.manyangled.breakable._ import com.manyangled.breakable._ scala> val bkb2 = for { | (x, xLab) <- Stream.from(0).breakable // create breakable sequence with a method | (y, yLab) <- breakable(Stream.from(0)) // create with a function | if (x % 2 == 1) continue(xLab) // continue to next in outer "x" loop | if (y % 2 == 0) continue(yLab) // continue to next in inner "y" loop | if (x > 10) break(xLab) // break the outer "x" loop | if (y > x) break(yLab) // break the inner "y" loop | } yield (x, y) bkb2: com.manyangled.breakable.Breakable[(Int, Int)] = com.manyangled.breakable.Breakable@34dc53d2 scala> bkb2.toVector res0: Vector[(Int, Int)] = Vector((2,1), (4,1), (4,3), (6,1), (6,3), (6,5), (8,1), (8,3), (8,5), (8,7), (10,1), (10,3), (10,5), (10,7), (10,9)) 

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

  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抛出一个exception。 我被困在这样的情况下:当抛出一个exception时,我必须打破循环,但是使用exception的scala.util.control.Breaks.break打破循环,并且它在catch块中被捕获。

我得到了丑陋的方法来解决这个问题:第一次做循环,并获得真正长度的计数。 并将其用于第二个循环。

当你使用一些Java库时,从Scala中取出rest并不是那么好。

我是Scala的新手,但是为了避免抛出exception和重复方法呢?

 object awhile { def apply(condition: () => Boolean, action: () => breakwhen): Unit = { while (condition()) { action() match { case breakwhen(true) => return ; case _ => { }; } } } case class breakwhen(break:Boolean); 

像这样使用它:

 var i = 0 awhile(() => i < 20, () => { i = i + 1 breakwhen(i == 5) }); println(i) 

如果你不想破坏:

 awhile(() => i < 20, () => { i = i + 1 breakwhen(false) }); 

巧妙地使用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)