首页 文章

如何摆脱Scala中的循环?

提问于
浏览
245

如何打破循环?

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循环转换为尾递归?

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

休息并继续Scala没有它们 . 为什么?他们有点必要;更好地使用许多较小的函数问题如何与闭包交互 . 他们不需要!

解释是什么?

17 回答

  • 2

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

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

    这在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
            }
        }
    }
    
  • 4

    打破for循环永远不是一个好主意 . 如果您使用for循环,则意味着您知道要迭代的次数 . 使用具有2个条件的while循环 .

    例如

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

    要添加Rex Kerr,请回答另一种方法:

    • (1c)你也可以在你的循环中使用一个守卫:
    var sum = 0
     for (i <- 0 to 1000 ; if sum<1000) sum += i
    
  • 12

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

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

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

  • 340

    只需使用while循环:

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

    你有三个(或左右)选项来摆脱循环 .

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

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

    除了你想要停止时(总和> 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旧版:

    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
    

    由于至少有三个我能想到的原因,故意这样做并不容易 . 首先,在大型代码块中,它会比实际上更多或更少,或者需要打破两个循环,你甚至可以注意到,所以如果你能够突破某些东西,那么你就不会发生任何问题 . [908503_ s "loops"不是't actually normal loops--they'方法调用有自己的循环,或者它们是递归,可能实际上也可能不是循环 - 尽管它们是循环的,但是很难想出一种一致的方法来知道"break"等应该做什么 . 所以,为了保持一致,更明智的做法就是不要有"break" .

    Note :所有这些都具有功能等价物,您可以返回 sum 的值,而不是将其变异 . 这些是更惯用的Scala . 但是,逻辑仍然是一样的 . ( return 变为 return x 等) .

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

  • 4

    这是一个尾递归版本 . 与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)
    }
    

    正如您所看到的,tr函数是外部for-comprehensions的对应部分,而内部函数的tr1则是tr1函数的对应部分 . 如果您知道优化我的版本的方法,欢迎您 .

  • 2

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

    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-iteration在没有新范围的情况下进行,产品生成以及条件都在for语句中完成(不是一个好的表达式 - 我找不到更好的表达式) . 条件是相反的,对于那个问题大小来说相当快 - 也许你可以通过更大的循环来获得一些东西 .

    String.reverse隐式转换为RichString,这就是我做2次额外反转的原因 . :)更多数学方法可能更优雅 .

  • 1

    第三方 breakable 包是一种可能的替代方案

    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))
    
  • 0

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

    def break(): Nothing = { throw breakException }
    

    最好的建议是:不要使用休息,继续和转到! IMO他们是相同的,坏的做法和各种问题的邪恶来源(和热门的讨论),最后“被认为是有害的” . 代码块结构化,在这个示例中,中断也是多余的 . 我们的Edsger W. Dijkstra†写道:

    程序员的素质是他们制作的程序中语句密度的递减函数 .

  • 58

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

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

    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 lib,其机制是ctx.read在无法找到任何内容时抛出异常 . 我陷入了这样的境地:我必须打破一个循环抛出了异常,但scala.util.control.Breaks.break使用Exception来打破循环,并且它在catch块中因此被捕获 .

    我得到了解决这个问题的丑陋方法:第一次进行循环并得到实际长度的计数 . 并将其用于第二个循环 .

    当您使用一些java库时,从Scala中取出并不是那么好 .

  • 1

    我是Scala的新手,但是如何避免抛出异常并重复方法:

    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)
    });
    
  • 0
    import scala.util.control._
    
    object demo_brk_963 
    {
       def main(args: Array[String]) 
       {
          var a = 0;
          var b = 0;
          val numList1 = List(1,2,3,4,5,6,7,8,9,10);
          val numList2 = List(11,12,13);
    
          val outer = new Breaks; //object for break
          val inner = new Breaks; //object for break
    
          outer.breakable // Outer Block
          {
             for( a <- numList1)
             {
                println( "Value of a: " + a);
    
                inner.breakable // Inner Block
                {
                   for( b <- numList2)
                   {
                      println( "Value of b: " + b);
    
                      if( b == 12 )
                      {
                          println( "break-INNER;");
                           inner.break;
                      }
                   }
                } // inner breakable
                if( a == 6 )
                {
                    println( "break-OUTER;");
                    outer.break;
                }
             }
          } // outer breakable.
       }
    }
    

    使用Breaks类打破循环的基本方法 . 通过声明循环可破坏 .

  • 0

    我们可以在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;
           } } } } }
    

    output :

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

相关问题