引き続きScalaのお勉強を。
記法の章は各種文法をどう記載するかを書いてあるだけなので飛ばして、今日はScalaの制御構文の章を。
制御構文
「構文」、「式」、「文」とは
Scalaの文法についてを把握する前に、「構文」、「式」、「文」の用語の解説があったのでこれについてまとめておく。
- 構文(Syntax) : プログラム言語内においてプログラムが構造を持つためのルール。前回出てきたUserクラスの記述全体がこれに当たる。
- 式(Expression) : プログラムを構成する部分のうち評価が成功すると値になるもの。例えば
1 + 1
など。 - 文(Statement) : 式とは逆に評価しても値にならないもの。Scalaの変数定義
val i = 1
は文
ブロック式
Scalaでは{}
で複数の式を囲むと、それ全体が式となる。
読み進めているドワンゴのScala資料ではこの{}
で括られた式を便宜上「ブロック式」と呼称する。
{ <式1>(;|<改行>) <式2>(;|<改行>) ... }
Scalaの言語仕様としてはただBlocksと書いてある(6.11 Blocks)。
BlockExpr ::= ‘{’ CaseClauses ‘}’ | ‘{’ Block ‘}’ Block ::= BlockStat {semi BlockStat} [ResultExpr]
下記の例では、ブロック式中の最後の式1 + 2
がブロック式の値となる。
scala> { println("A"); println("B"); 1 + 2; } A B res0: Int = 3
上記ブロック式の構文に従うとメソッドの呼び出しprintln
も式になるがそれでいいのか気になったので別に試してみた。
scala> { println("A"); println("B");} A B
とりあえず、上記のようにprintln
だけでブロック式を終わらせると1 + 2
のようにres0: Int = 3
という結果が表示されない。
探してみると以下の記事で丁寧に解説してくださっていた。
printlnなどの一見、値を返さなそうなメソッドがあります。しかしScalaではメソッド呼び出しは、式として判別されるため、意味のある結果を返さないUnit型として判断されます。
とのこと。このようなものの結果型を調べるためには:t
を式の前につけるといいらしい。
scala> :t println("B");
Unit
なるほど、確かにUnit
が返ってきた。
if式
「if文」ではなく「if式」なのね。形式はJavaと一緒(らしい)。
C言語とかとも一緒。
if '('<条件式>')' <then式> (else <else式>)?
条件式
はBoolean
型else
項は省略可能
scala> var age = 18 age: Int = 18 scala> if(age < 18) { | "18歳未満です" | } else { | "18歳以上です" | } res1: String = 18歳以上です
上記で書いたようにif
式なので値が返却される。else
項を省略した場合は先程紹介したUnit
が補われた形で扱われる。このUnit
はJavaではvoid
に相当するもので返すべき値が無いときに使用され()
を値として持つ。
以下、ScalaのStandard LibraryのUnitの項目からの引用。
Unit
is a subtype of scala.AnyVal. There is only one value of typeUnit
,()
, and it is not represented by any object in the underlying runtime system. A method with return typeUnit
is analogous to a Java method which is declaredvoid
.
練習問題
var age: Int = 5
という年齢を定義する変数とvar isSchoolStarted: Boolean = false
という就学を開始しているかどうかという変数を利用して、 1歳から6歳までの就学以前の子どもの場合に“幼児です”と出力し、それ以外の場合は“幼児ではありません”と出力するコードを書いてみましょう。
解答
scala> var age: Int = 5
age: Int = 5
scala> var isSchoolStarted: Boolean = false
isSchoolStarted: Boolean = false
scala> if((1 <= age) && (age <= 6) && (!isSchoolStarted)) {
| println("幼児です")
| } else {
| println("幼児ではありません")
| }
幼児です
while式
while '(' <条件式> ')' 本体式
if
式と一緒でwhile
も式になる。返却すべき値がないのでUnit
型の()
が返却される。
scala> while (i <= 10) { | println("i = " + i) | i = i + 1 | } i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 10
do while
式もある。
scala> do { | println("i = " + i) | i = i + 1 | } while (i < 10) i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9
Scalaにはbreak
文やcontinue
文は無いらしい。ただ後ほど扱う高階関数を使えばあまり必要にならないとのこと。
練習問題
do while
を利用して、0から数え上げて9まで出力して10になったらループを終了するメソッドloopFrom0To9
を書いてみましょう。loopFrom0To9
は次のような形になります。???
の部分を埋めてください。
def loopFrom0To9(): Unit = { var i = ??? do { ??? } while(???) }
解答
def loopFtom0To9(): Unit = {
var i = 0
do {
println("i = " + i)
i = i + 1
} while (i < 10)
}
for式
構文は以下の通り。
for '(' (<ジェネレータ>;)+ ')' '<本体>'
ジェネレータを1つ以上使うことが出来る。またジェネレータは以下の通り。
<ジェネレータ> = x <- <式>
言語仕様によると、Guard条件も含めれるみたい。
Expr1 ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr Enumerators ::= Generator {semi Generator} Generator ::= Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} Guard ::= ‘if’ PostfixExpr
さっそく例を。
scala> for (x <- 1 to 5; y <- 1 until 5) { | println("x = " + x + " y = " + y) | } x = 1 y = 1 x = 1 y = 2 x = 1 y = 3 x = 1 y = 4 x = 2 y = 1 x = 2 y = 2 x = 2 y = 3 x = 2 y = 4 x = 3 y = 1 x = 3 y = 2 x = 3 y = 3 x = 3 y = 4 x = 4 y = 1 x = 4 y = 2 x = 4 y = 3 x = 4 y = 4 x = 5 y = 1 x = 5 y = 2 x = 5 y = 3 x = 5 y = 4
for
式は他の言語とはだいぶ違って独特。ジェネレータを複数持てるのでループ構造がシンプルになるのはいい感じ。pythonのzip
関数が一番近いかな。ちなみにto
とuntil
の違いは以下のように示す範囲の違いにあるようだ。
表現 | 範囲 |
---|---|
1 to 10 |
1から9まで |
1 until 10 |
1から10まで |
以下のようにList
(コレクションの一つ)を一つずつ辿って処理することも出来る。
scala> for(e <- List("A", "B", "C", "D", "E")) println(e) A B C D E
perlやpythonでよく使う形のループ処理も可能ということか。これだけでも結構強力な処理が可能だな。
上記のコレクションを加工することにも使えるという例もあった。
scala> for(e <- List("A", "B", "C", "D", "E")) yield { | "Pre" + e | } res0: List[String] = List(PreA, PreB, PreC, PreD, PreE)
yield
を使うことで、”コレクションの要素を加工して返す”という処理にすることが出来て、この動きをfor-comprehensionと呼ぶことがある。
練習問題
1から1000までの3つの整数a, b, cについて、三辺からなる三角形が直角三角形になるような
a
,b
,c
の組み合わせを全て出力してください。直角三角形の条件にはピタゴラスの定理を利用してください。 ピタゴラスの定理とは三平方の定理とも呼ばれ、a ^ 2 == b ^ 2 + c ^ 2
を満たす、a
,b
,c
の長さの三辺を持つ三角形は、直角三角形になるというものです。
愚直に解答
for (a <- 1 until 1000; b <- 1 until 1000; c <- 1 until 1000) {
if ((a * a) == (b * b) + (c *c)) {
println("(a, b, c) = (" + a + ", " + b + ", " + c + ")")
}
}
if
式使って書いたら、解答はGuard条件を使ったもっとスマートなものだった。
ひとつ気になってScalaのべき乗の書き方を調べてみたが、pythonの**
のように演算子を使って書くものはなくMath
ライブラリを使うみたい。
この章は残りはmatch
式のみだが結構な分量がありそうなので今日はここまでにして残りは次回。