【入門編】基礎から学ぶScala②:制御構文(if/for/while/return)

言語学習

プログラミングにおいて、条件分岐や繰返しなど、制御構文は重要な役割を持っています。

関数型・オブジェクト指向のマルチパラダイムをサポートするScalaですが、制御構文はほかの言語と比較しても突飛なところはあまりありません。

式と文、ブロック

まずはじめに、ScalaはJavaなどの手続き的な言語と比較すると、式として評価される構文がかなり多い言語です。そのため、ほかの言語では見られないようなコードの書き方も多く見かけるようになるでしょう。

式(Expression)」は評価すると値を返す構文要素を指します。
以下のような計算式は、計算の結果として「6」という値を返します。

1 + 2 + 3
println(1 + 2 + 3) // 6

文(Statement)」は評価しても値を返しません。
例えば、以下のような変数ヘの代入構文は変数valueに右辺「1 + 2」の評価結果を代入するものですが、この構文自体は値を返すものではありません。

val value = 1 + 2

ブロック」はほかの言語でもよく使われるような、波かっこ({})で囲われた1つ以上の式の塊を指します。ほかの言語においては、ブロックは評価しても値を返さないことがほとんどですが、Scalaではブロックも式として値を返します。

Scalaではブロックの中で一番最後に評価された式の返り値がブロックの値となります。

val value = {
    val a = 10
    val b = 20
    a + b
}
println(value) // 30

if式

「if文」とは書かず「if式」と記述したことに違和感を覚えた方もいるかもしれないですね。

Scalaでは、ifも評価結果を返す式として扱われます。

構文としては、そのほかの手続き的な言語とさほど変わらないので、使い方に困ることはないと思います。ただし、Javascriptなどとは異なりifで評価することができる値はBooleanだけなので気を付けましょう

val a = 10
val b = 20

if (a == 10) {
    println(a)
}
// 括弧を省いて、thenを使うこともできる
if b == 20 then {
    println(b)
}
// ifで判定できるのはBooleanだけ。以下のコードはエラー
if (1) {
    println("hello")
}

while式

Scalaでは「while」によるループ処理を、Javaと同様の記法で実現できます。

Java同様に、条件式がBooleanに評価される間、本体部分の処理を繰り返し実行します。

while ( <条件式> ) <本体部分>
// Scala3以降では「do」を使った記法も可能
while ( <条件式> ) do <本体部分>

var v = 0
while (v < 10) do
  v = v + 1
  println(v)

for式

Scalaでは「for」も式です。
一般的な手続き型言語と同様の記法でループを表現できます。

for のジェネレータ部分には、「;」区切りで複数のジェネレータを記載することも可能。
ただし、複数のジェネレータを定義する場合には「()」の代わりに「{}」を使うことも可能で、この場合は、各ジェネレータごとの「;」は不要になります。

さらに、ジェネレータの後にifを付与することで、条件に合致した場合だけブロックの処理を実行することもできます(フィルター)。

Scalaのforは以下のような構文で利用します。

for ( <ジェネレータ> ) <本体部分>
// Scala3以降では「do」を使った記法も可能
for ( <ジェネレータ> ) do <本体部分>

for (x <- 1 to 5) println(x)       // toなら1~5の間
for (x <- 1 until 5) println(x)    // untilなら1~4の間
for (x <- 1 to 10 by 2) println(x) // by でステップを定義可能

// 複数ジェネレータの指定も可能
for (x <- 1 to 5; y <- 10 until 15 if y % 2 == 0) {
  println(x) // 本体部分は複数行も可
  println(y)
}

// 「()」の代わりに「{}」を使うと、「;」を省略できる
for {
  x <- 1 to 5
  y <- 10 until 15 if y % 2 == 0
} {
  println(x)
  println(y)
}

また、通常のfor式では返り値はUnitなのですが、「yield」キーワードを使用することで、for式の本体部での実行結果を新しいコレクションとして取り出すこともできます。

// yeildすると新しいコレクションとして値を返せる
val evens = for (x <- 1 to 10 if x % 2 == 0) yield x
println(evens) // Vector(2, 4, 6, 8, 10)

return式

return式はメソッドから値を返す場合に使うことができます。

これだけ聞くと、JavaやC言語をはじめとした他言語と同じ役割のように思われますが、それらの言語と異なり、Scalaでは多くの場合return式は必要になりません

def sum(numbers: List[Int]): Int = {
    var result = 0
    for (v <- numbers) result += v
    result
}
println(sum(List(1,2,3,4,5))) // 15

上記は、整数のリストの値の総和を求める関数sumの定義です。

ここで、一行目の 「=」 の右側はブロックであり、このブロックは値を返す式として評価されます。

すでに述べている通り、Scalaのブロックは一番最後に評価された式の返値をブロックの評価値として返すので、上記関数ではresultの値を結果として返します。

Scalaでreturn式が必要になるのは、以下のように処理を途中で中断して値を返したい場合などです。

def getNumberIndex(numbers: List[Int], number: Int): Int = {
    var index = 0
    for (i <- numbers) {
      if (i == number) {
        return index
      }
      index += 1
    }
    -1
  }
  val ls = List[Int](1,2,7,3,5,9)
  println(getNumberIndex(ls, 7))

まとめ

  • Scalaでは、if/for/whileなどの制御構文は式として評価され、値を返す
  • メソッドの定義などで登場するブロック({ … })も式なので値を返す

Scalaでは、ほかの言語では「文」として表現される構文要素の多くが「式」と呼ばれています。

この特性を知っていることで、ほかの言語にはない表現をすることも可能になるので、覚えておきましょう。

コメント

  1. haru04 haru04 より:

    テストコメント

タイトルとURLをコピーしました