web-dev-qa-db-ja.com

Scalaでループから抜け出すにはどうすればいいですか?

どうやってループを抜けますか?

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にはそれらがありません。どうして?それらは少し必須です。多くの小さな関数をうまく使うクロージャとどのようにやり取りするかを発行します。必要ありません。

説明は何ですか?

258
TiansHUo

ループから抜け出すには、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 =>
}

(2a)Scala 2.8+では、C/Javaの使い慣れた古いブレークによく似た構文を使用して、scala.util.control.Breaksに既にプリパッケージされています。

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の「ループ」のほとんどは、実際には通常のループではありません。独自のループを持つメソッド呼び出し、または実際にはループである場合もそうでない場合もありますが、それらはactループのように、「ブレーク」などが何をすべきかを知る一貫した方法を見つけるのは困難です。したがって、一貫性を保つために、賢明なことは、「ブレーク」をまったく持たないことです。

sumの値をその場で変更するのではなく返す場合、これらすべてと機能的に同等です。これらはより慣用的なScalaです。ただし、ロジックは同じままです。 (returnreturn xなどになります)。

358
Rex Kerr

これは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
        }
    }
}
60
hohonuuli

Forループから抜け出すことはお勧めできません。 forループを使用している場合は、何回繰り返したいかを知っているということです。 2つの条件でwhileループを使用してください。

例えば

var done = false
while (i <= length && !done) {
  if (sum > 1000) {
     done = true
  }
}
29
Keith Blanchard

Rex Kerrに別の答えを追加するには:

  • (1c)あなたのループにもガードを使うことができます。

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

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

Scala 2.8には壊れる方法が含まれています

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

6
Ham Vocke

Whileループを使うだけです:

var (i, sum) = (0, 0)
while (sum < 1000) {
  sum += i
  i += 1
}
5
pathikrit
// 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

5
user1836270

最初に範囲全体を生成してからIteratorを使用して範囲内で反復するのではなく、範囲を超えて反復条件に応じて範囲を超えて値を生成するアプローチ(Streamの使用にヒントを得たもの)

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

これが末尾再帰バージョンです。 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は内側のものの対応物です。私のバージョンを最適化する方法を知っていればあなたは大歓迎です。

4
fresskoma

サードパーティの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))
2
eje

単に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
2
Viraj.Hadoop

あなたの解決策に近いのはこれでしょう:

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回の追加リバースを行う理由です。 :)より数学的なアプローチはよりエレガントかもしれません。

2
user unknown

巧妙コレクションのための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)
1
AlvaPan

以下は簡単な方法でループを破るためのコードです

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"

        }
      }
    }
  }
}
1
Nilesh Shinde

皮肉なことに、scala.util.control.BreaksのScalaブレークは例外です。

def break(): Nothing = { throw breakException }

最善のアドバイスは次のとおりです。breakを使用せず、続行して、ジャンプします。 IMOは、同じような悪い習慣であり、あらゆる種類の問題(および熱い議論)の邪悪な原因であり、最後に "有害であると考えられています"。この例でもコード化されたコードブロックは不要です。私たちのEdsger W. Dijkstra†は書きました:

プログラマーの質は、彼らが作り出すプログラムのステートメントに行くの密度の減少関数です。

1
Epicurist

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

 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をスローすることです。例外がスローされたときにループを中断しなければならないが、ループを中断するために例外を使用してscala.util.control.Breaks.breakが発生したため、catchブロック内にあったため、捕捉されました。

私はこれを解決するために醜い方法を得ました:最初にループをして、そして本当の長さのカウントを得る。そしてそれを2番目のループに使います。

いくつかのJavaライブラリを使用している場合、Scalaからブレークアウトするのはそれほど良くありません。

0
Lucas Liu

過去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

最大のパリンドロームを見つけるには(とにかくリスト全体をチェックする必要があるため、怠inessはあなたをあまり買わない):

palindromes.max

元のコードは、実際には後続の製品よりも大きい最初の回文をチェックしています。これは、意図しないと思われる奇妙な境界条件を除いて、最初の回文をチェックするのと同じです。製品は厳密に単調減少していません。たとえば、998*998999*997よりも大きいですが、ループのかなり後で表示されます。

とにかく、分離された遅延生成と条件チェックの利点は、リスト全体を使用しているように書くことですが、必要なだけ生成することです。両方の長所を最大限に活用できます。

0
Karl Bielefeldt

私は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
Mohamad Alallan
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クラスを使用してループを分割する基本的な方法。ループを分割可能として宣言します。