[AD] Scalaアプリケーションの開発・保守は合同会社ミルクソフトにお任せください
この記事では、式とブロック式(以降、ブロックと記載)について解説します。
また、Scalaの制御構文を例に、式やブロックの使い方を解説します。
式、文、ブロックについて
式は評価すると値となる
プログラムを構成する構文のうち、評価すると数値や文字など何かしらの値となるものを式といいます。
以下はすべて式です。
1 + 1
hogehoge
if(x < 10) x * 2
文は評価しても値とならない
プログラムを構成する構文のうち、評価しても値とならないもののことを文といいます。
Scalaでは、val
やvar
が文になります。
val s: String = "hogehoge"
は、「変数 s」を定義して、sに文字列: ”hogehoge”を設定しますが、この定義自体は値にはなりませんので、文です。
ブロックは評価すると値となる
次にブロックについて説明します。
波括弧{}
で囲んだ1つ以上の式のことをブロックといいます。
波括弧{}
内に記載した最後の式を評価した値が、ブロックの値になります。
以下の例では、ブロック内の最後の式(a + b
)を評価した値が、ブロックの値になります。
val x = val a = 1 val b = 2 a + b println(x)
実行結果は、以下のように3
が出力されます。
3
()、{}の違いについて
では、括弧()
で式を囲んだ場合は、どのような動作になるでしょうか?
括弧()
で式を囲んだ場合も、式を評価した値となりますが、波括弧{}
と違って、式は1つだけしか書くことができません。
ブロックの例のように、a
とb
を足した値を求める場合、以下のように記載することはできますが、
val a = 1 val b = 2 val x = (a + b)
以下のように書くことはできません。
val x = ( val a = 1 val b = 2 a + b )
このように書いた場合は、コンパイルエラーになります。
error: illegal start of simple expression
制御構文は値を返す
次に制御構文について説明します。
Scalaの制御構文はすべて式のため、値を返します。
一見、値を返さないように見えるwhile
やdo-while
などもUnit
を返します。
Unit
とは、戻り値としては意味がないことを示すものです。
次からは、Scalaで使用できる制御構文を紹介します。
ここでは概要のみを紹介しますので、詳細は各制御構文の説明を参照してください。
ifの戻り値
ifは、以下のように記述します。
Scalaif(条件式) 式1 else 式2
条件式がtrue
の場合は、式1を評価した値がif
の値になります。
条件式がfalse
の場合は、式2を評価した値がif
の値になります。
以下の例では、20歳未満の場合には、「○○さんは、お酒を飲んではいけません。」を返し、20歳以上の場合には、「○○さんは、お酒を飲めます。」を返します。
def whoCanDrink(person: Person): String = if(person.age < 20) s"${person.name}さんは、お酒を飲んではいけません。" else s"${person.name}さんは、お酒を飲めます。"
では、定義した関数を呼び出してみます。
Taroさんは、20歳なのでif
の値は、「Taroさんは、お酒を飲めます。」となります。
println(whoCanDrink(Person(20, "Taro")))
出力結果は、以下のとおりです。
Taroさんは、お酒を飲めます。
Hanakoさんは、20歳に満たないのでif
の値は、「Hanakoさんは、お酒を飲んではいけません。」となります。
println(whoCanDrink(Person(19, "Hanako")))
出力結果は、以下のとおりです。
Hanakoさんは、お酒を飲んではいけません。
上記例では、式1、式2には、ブロックを使用していますが、ブロックの他に式、括弧を指定することができます。
ただし、式や括弧は三項演算として利用する場合にのみ使用し、通常はブロックを使用するのが良いでしょう。
if式について、詳しい使い方は以下の記事を参照してください。
forの戻り値
for
の基本的な構文は、以下のようになります。
Scalafor(ジェネレーター) 本体
ジェネレータと本体には、式やブロックを指定できます。
次の例では、if
で登場したTaroさんとHanakoさんからなるリストに対して、for
を適用しています。
personsの要素ごとに本体を適用して、Seq[String]
型の値を取得しています。
val persons = Seq( Person(20, "Taro"), Person(19, "Hanako") ) val result: Seq[String] = for(p <- persons) yield s"${p.name}さんは、${p.age}歳です。" result.foreach(e => println(e))
実行結果は、以下のようになります。
Taroさんは、20歳です。 Hanakoさんは、19歳です。
上記例では、本体の前にyield
を記述しているため、本体を評価した結果から値を生成していますが、本体の前にyield
を記述しない場合は、返す値がないため、for
の値はUnit
となります。
for
については、その他に便利な利用方法が多数あります。
詳細は、以下の記事を参照してください。
matchの戻り値
match
は、以下のように記述し、パターンにマッチした式を評価します。
Scala対象 match { case パターン => 式 ... }
以下の例では、Person.name
の値によって"太郎"、"花子"、Person.name
を返す関数を定義しています。
def getJapaneseName(person: Person): String = person.name match case "Taro" => "太郎" case "Hanako" => "花子" case name => name
では、定義した関数を呼び出してみます。
以下の場合、Person.name
が、"Taro"のため、match
の戻り値は、"太郎"となります。
println(getJapaneseName(Person(20, "Taro")))
実行結果は、以下のようになります。
太郎
以下の場合、Person.name
が、"Hanako"のため、match
の戻り値は、"花子"となります。
println(getJapaneseName(Person(19, "Hanako")))
実行結果は、以下のようになります。
花子
上記例は、単純なパターンマッチの例ですが、Scalaのmatch
は、それ以外に型によるパターンマッチや、パターンマッチによる値の取り出し等、便利な使い方があります。
詳細は、match
の説明の記事を参照してください。
whileの戻り値
while
は、以下のように記述します。
Scalawhile(条件式) 式
while
は、条件式がtrue
の間、式を評価します。
条件式がfalse
になったら、評価を中止します。
while
も式なのですが、返すべき値がないため、while
の値は、Unit
です。
以下の例では、1から9の数値を加算した値を求めています。
「変数 x
」の値は、1から9までの数値を加算した54ですが、「変数 y
」の値は、Unit
です。
var x = 0 var i = 1 val y = while(i < 10) i = i + 1 x = x + i println(s"x=${x}") println(s"y=${y}")
実行結果は、以下のようになります。
x=54 y=()
do-whileの戻り値(Scala 2系のみ)
Scala 3ではdo-while
はサポートされませんので、新規に利用するのは控えましょう。
Scala 3でもScalaのコンパイラオプションに-language:Scala2Compat
を指定すると、do-while
を使用することができます。
さらに、-rewrite
を指定すると、コンパイラが自動的に該当箇所を書き換えてくれます。ぜひ活用しましょう。
Scalapediaでは既にScala2系のサポートを終了しましたが、引き続きScala 2系を扱う必要のある方向けに説明を残しておきます。
さて、Scala 2系にはJavaとの互換性のため、do-while
が用意されていました。
do-while
もwhile
と同じような動作をします。
while
と違うところは、式を評価してから条件式を評価するため、最低1回は式を評価することです。
do-while
は、以下のように記述します。
Scalado 式 while(条件式)
以下の例では、条件式にfalse
を指定していますが、式を評価してから、条件式を評価するため、「変数 x
」は4となります。
「変数 y
」の値は、while
と同じUnit
です。
var x = 2 val y = do { x = x * 2 } while(false) println(s"x=${x}") println(s"y=${y}")
実行結果は、以下のようになります。
x=4 y=()