296

ループを解除するにはどうすればよいですか?

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ループを末尾再帰に変換するにはどうすればよいですか?

22ページのFOSDEM2009http ://www.slideshare.net/Odersky/fosdem-2009-1013261でのScalaTalkから:

中断して続行Scalaにはそれらがありません。なんで?それらは少し必須です。多くの小さな関数をより適切に使用するクロージャーと対話する方法を発行します。それらは必要ありません!

説明は何ですか?

4

19 に答える 19

392

ループから抜け出すための3つ(またはそれくらい)のオプションがあります。

合計が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 =>
}

scala.util.control.Breaks(2a)Scala 2.8以降では、これは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

これは、私が考えることができる少なくとも3つの理由から、意図的に簡単になりすぎないようにしています。まず、大きなコードブロックでは、「continue」および「break」ステートメントを見落としたり、実際よりも多かれ少なかれ脱出していると考えたり、実行できない2つのループを破る必要があります。とにかく簡単に-したがって、標準的な使用法は便利ですが、問題があるため、コードを別の方法で構造化する必要があります。第二に、Scalaにはおそらく気付かないようなあらゆる種類の入れ子があるので、何かから抜け出すことができれば、コードフローがどこで終わったか(特にクロージャーの場合)におそらく驚かれることでしょう。第三に、Scalaの「ループ」のほとんどは実際には通常のループではなく、独自のループを持つメソッド呼び出しです。ループのように、「ブレーク」などが何をすべきかを知るための一貫した方法を思い付くのは難しいです。したがって、一貫性を保つために、より賢明なことは、「休憩」をまったく持たないことです。

:これらすべての機能に相当するものがあり、値を所定の位置で変更するのではsumなく、の値を返します。これらはより慣用的なScalaです。ただし、ロジックは同じままです。(などにreturnなりreturn xます)。

于 2010-04-30T07:29:38.513 に答える
73

これは、ブレークを使用するメカニズムを備えたScala2.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
        }
    }
}
于 2011-02-28T05:07:02.940 に答える
41

forループから抜け出すことは決して良い考えではありません。forループを使用している場合は、反復する回数がわかっていることを意味します。2つの条件でwhileループを使用します。

例えば

var done = false
while (i <= length && !done) {
  if (sum > 1000) {
     done = true
  }
}
于 2014-08-01T23:16:27.433 に答える
15

Rex Kerrを追加するには、別の方法で答えてください。

  • (1c)ループでガードを使用することもできます。

     var sum = 0
     for (i <- 0 to 1000 ; if sum<1000) sum += i
    
于 2010-04-30T07:56:39.540 に答える
7

Scalaにはまだないので、-ステートメントbreakを使用してこの問題を解決することができますreturn。したがって、内部ループを関数に入れる必要があります。そうしないと、returnはループ全体をスキップします。

ただし、Scala2.8にはブレークする方法が含まれています

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

于 2010-04-30T06:35:20.147 に答える
7

最初に範囲全体を生成してから、を使用して反復するのではなく、破壊条件まで反復するときに範囲全体の値を生成するアプローチIterator(@RexKerrの使用に触発されたStream

var sum = 0
for ( i <- Iterator.from(1).takeWhile( _ => sum < 1000) ) sum += i
于 2015-02-12T10:17:30.830 に答える
6

単純に私たちは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
于 2018-07-20T11:56:04.553 に答える
5
// 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

于 2014-03-10T04:37:17.880 に答える
5

whileループを使用するだけです。

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}
于 2014-04-21T22:38:12.473 に答える
4

これが末尾再帰バージョンです。内包表記と比較すると、確かに少しわかりにくいですが、機能的だと思います:)

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は内側の内包表記の対応物です。私のバージョンを最適化する方法を知っていれば大歓迎です。

于 2011-06-06T00:20:40.057 に答える
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反復は新しいスコープなしで行われ、製品の生成と条件はforステートメントで行われます(適切な表現ではありません-より良い表現は見つかりません)。条件が逆になり、その問題のサイズではかなり高速になります。ループが大きくなると、ブレークで何かが得られる可能性があります。

String.reverseは暗黙的にRichStringに変換されます。そのため、2つの追加のリバースを実行します。:)より数学的なアプローチはよりエレガントかもしれません。

于 2010-05-18T06:58:06.797 に答える
2

私は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)
});
于 2016-01-21T09:19:37.953 に答える
2

サードパーティbreakableパッケージは1つの可能な代替手段です

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))
于 2017-03-05T19:58:44.930 に答える
2
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クラスを使用してループを解除する基本的なメソッド。ループをブレーク可能として宣言する。

于 2018-05-10T10:22:22.840 に答える
1

皮肉なことに、Scalaの侵入scala.util.control.Breaksは例外です。

def break(): Nothing = { throw breakException }

最善のアドバイスは次のとおりです。breakを使用せず、続行してgoto!IMOは同じで、悪い習慣であり、あらゆる種類の問題(および熱い議論)の邪悪な原因であり、最終的には「有害であると見なされます」。構造化されたコードブロック。この例でも、ブレークは不要です。私たちのエドガー・W・ダイクストラ†は次のように書いています。

プログラマーの質は、彼らが作成するプログラムのgotoステートメントの密度の減少関数です。

于 2014-06-23T08:00:22.790 に答える
1

以下のコードのような状況になりました

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

私はJavalibを使用していますが、ctx.readが何も見つからない場合に例外をスローするメカニズムがあります。例外がスローされたときにループを中断する必要があるという状況に陥りましたが、例外を使用してループを中断するscala.util.control.Breaks.breakであり、catchブロック内にあったためにキャッチされました。

私はこれを解決するための醜い方法を手に入れました:初めてループを実行し、実際の長さのカウントを取得します。2番目のループに使用します。

いくつかのJavaライブラリを使用している場合、Scalaから休憩を取るのはそれほど良くありません。

于 2015-10-08T10:23:18.183 に答える
1

収集のための方法の巧妙な使用は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)
于 2016-02-26T19:27:11.337 に答える
1

以下は簡単な方法でループを壊すコードです

import scala.util.control.Breaks.break

object RecurringCharacter {
  def main(args: Array[String]) {
    val str = "nileshshinde";

    for (i <- 0 to str.length() - 1) {
      for (j <- i + 1 to str.length() - 1) {

        if (str(i) == str(j)) {
          println("First Repeted Character " + str(i))
          break()     //break method will exit the loop with an Exception "Exception in thread "main" scala.util.control.BreakControl"

        }
      }
    }
  }
}
于 2018-12-16T12:50:12.857 に答える
1

過去9年間でScalaのスタイルがどの程度変化したかはわかりませんが、既存の回答のほとんどがvars再帰を使用している、または読みにくいというのは興味深いことです。早期に終了するための鍵は、レイジーコレクションを使用して候補を生成し、条件を個別に確認することです。製品を生成するには:

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が、ループのかなり後の方に表示されます。

とにかく、分離された遅延生成と条件チェックの利点は、リスト全体を使用するのとほとんど同じように記述できることですが、必要なだけ生成されます。あなたは一種の両方の世界の長所を手に入れます。

于 2019-08-15T21:23:14.113 に答える