組み込み制御構造のfor式

6274 ワード

Scalaのfor式は列挙操作の「スイス軍刀」で、いくつかの簡単な成分を異なる方法で組み合わせて様々な列挙を表現することができます.
列挙集合クラスForができる最も簡単なことは,集合中のすべての要素を列挙することである.例:
package scalaTest
object Test4 {
    def main(args:Array[String]) {
        val fileHere = (new java.io.File(".")).listFiles
        for(file <- fileHere) println(file)
    }
}

発生器と呼ばれる構文「file<-filesHere」を用いて,filesHereの要素を遍歴した.列挙するたびにfileという新しいvalが要素値によって初期化されます.コンパイラは、fileのタイプがFileであることを推定することができる.なぜなら、file HereはArray[File]であるからである.各列挙について、for式の関数体println(file)が1回実行されます.FileのtoStringメソッドはファイルまたはディレクトリの名前を生成するため、現在のディレクトリのすべてのファイルとディレクトリの名前が印刷されます.For式構文は配列だけでなく、任意の種類の集合クラスに有効です.例えば、「1 to 5」のような文法を使ってRangeを簡単に作成し(これは後で説明します)、forで列挙することができます.
package scalaTest
object Test4 {
    def main(args:Array[String]) {
        for(i <- 1 to 9) println("Iteration " + i)
    }
}

列挙されたRangeの上境界を含めたくない場合は、例えば、toの代わりにuntilを使用することもできます.
package scalaTest
object Test4 {
    def main(args:Array[String]) {
        for(i <- 1 until 9) println("Iteration " + i)     //    
    }
}

フィルタリングは、集合のすべての要素を列挙するのではなく、あるサブセットをフィルタリングしたい場合があります.これは、for式のカッコにフィルタ(if句)を追加することによって実現できます.例:
package scalaTest
object Test4 {
    def main(args:Array[String]) {
        for(i <- 1 until 9 if i % 2 == 0) println("Iteration " + i)
    }
}

あるいは、以下のように書くこともできます.
package scalaTest
object Test4 {
    def main(args:Array[String]) {
        for(i <- 1 until 9)
            if(i % 2 == 0) println("Iteration " + i)
    }
}

このコードは命令的な背景のプログラマーにとってもっとよく知られているように見えます.しかしながら、命令式のフォーマットは、オプションの実装方法の1つにすぎない.For式が「式」と呼ばれるのは、for式<-句の集合に依存する意味のある値を生成できるためです.必要に応じて、より多くのフィルタを含めることができます.if句をどんどん追加すればいいだけです.例:
package scalaTest
object Test4 {
    def main(args:Array[String]){
        for(
            i <- 1 until 9
            if i % 2 == 0;    //       
            if i % 4 == 0
        ) println("Iteration " + i)
    }
}

注意:発生器にフィルタを1つ以上入れる場合、if句はセミコロンで区切らなければなりません(注:scala 2.10.7バージョンではセミコロンは使わないようです!!).
ネスト列挙複数の<-句を加えると、ネストされた「ループ」が得られます.次の例を示します.
package scalaTest
object Test4 {
    def main(args:Array[String]){
        val arr = Array(Array(1,2,3,4,5),Array(11,12,13,14,15))
        for(
            ar <- arr
            if ar(0) > 10;    //        
            a <- ar
            if a % 2 == 0 
        )println("Iteration " + a)
    }
}

注:ネストされた列挙間もセミコロンで区切らなければなりません.よろしければ、発生器とフィルタを括弧で包む代わりに括弧を使うことができます.カッコを使うメリットは、カッコを使うときにつけなければならないセミコロンを省略できることです(もちろんセミコロンを外さなくてもいいです).例:
package scalaTest
object Test4 {
    def main(args:Array[String]){
        val arr = Array(Array(1,2,3,4,5),Array(11,12,13,14,15))
        for{        //    ,     
            ar <- arr if ar(0) > 10
            a <- ar if a % 2 == 0
        }println("Iteration " + a)
    }
}

ストリーム間変数バインド
package scalaTest
object Test4 {
    def main(args:Array[String]){
        val arr = Array(Array("  a","b"," c "),Array("D","  E","F   "))
        for{
            ar <- arr if ar(0).trim == "a"
            a <- ar if a.trim == "c"
        }println(a.trim)
    }
}

前のコードセグメントに式a.trimが繰り返し表示されていることに注意してください.これは無視できない計算なので、一度だけ計算したいかもしれません.これは,結果を等号(=)で新しい変数にバインドすることによって実現できる.バインドされた変数はvalとして導入および使用されますが、例のようにキーワードvalはありません.
package scalaTest
object Test4{
    def main(args:Array[String]){
        val arr = Array(Array("a","b  ","  c  "),Array("D","  E","F   "))
        for{
            ar <- arr if ar(0).trim == "a"
            a <- ar            //  ,      ,     
            k = a.trim if k == "c"
        }println(k)
    }
}

改行しない、プラス記号の例:
package scalaTest
object Test4 {
    def main(args:Array[String]) {
        val arr = Array(Array(" a","b "," c "),Array("D","  E","F  "))
        for{
            ar <- arr if ar(0).trim == "a"
            a <- ar;k = a.trim if k == "c"    //      
        }println(k)
    }
}

コードでは,kという変数が途中からfor式に導入され,a.trimの結果値に初期化される.その後のfor式は2つの場所で新しい変数を使用し,1つはif,もう1つはprintlnである.
新しいコレクションを作成する前に、すべての例が列挙値を操作して解放されるだけで、for式の後にキーワードyieldを付けるだけで、毎回の反復を覚える値を作成することもできます.例:
package scalaTest
object Test4 {
    def main(args:Array[String]){
        val arr = Array(Array(" a","b "," c "),Array("D","  E","F  "))
        //    
        var arr2 = for{
            ar <- arr if ar(0).trim == "a"
            a <- ar;k = a.trim if k == "c" || k == "b"
        }yield k
        for(s <- arr2) println("     : " + s)
    }
}

また例:
package scalaTest
object Test4{
    def main(args:Array[String]){
        val arr = Array(Array(" a","b "," c "),Array("D","  E","F  "))
        def fuOne(arr:Array[Array[String]]) = for{
            ar <- arr if ar(0).trim == "a"
            a <- ar;k = a.trim if k== "c" || k == "b"
        }yield k
        for(s <- funOne(arr)) println("     : " + s)
    }
}

For式は、実行するたびに新しい値を生成します.この例ではkです.for式が完了すると、結果は生成値をすべて含む集合オブジェクトになります.オブジェクトのタイプは、列挙句処理の集合タイプ(前例ではArray[String]であり、ここでは1次元)に基づいている.また、yieldキーワードを置く場所に注意してください.for-yield式の構文は、for{句}yield{ループ体}の例です.
package scalaTest
object Test4 {
    def main(args:Array[String]){
        val arr = Array(Array(" a","b "," c "),Array("D","  E","F  "))
        def funOne(arr:Array[Array[String]]) = for{
            ar <- arr if ar(0).trim == "a"
            a <- ar;k = a.trim if k == "c" || k == "b"
        }yield{
            println("~~~~~" + k)
            k
        }
    }
}

yieldは循環体全体の後にある.ループがカッコで囲まれたコードブロックであっても、yieldは必ず左カッコの後に置きます.これまでscalaのfor式のすべての主要な特徴を見たことがありますが、この部分は本当に速いので、後で詳しく紹介します.