S-JIS[2010-10-23/2017-02-02] 変更履歴
Scalaの文(ステートメント・statement)のメモ。
Scalaでは、厳密には、ifやwhile・for、return等も「文」ではなく「式」という扱いになっている。
つまり何らかの値を返す。
コメントの書き方は、C++やJavaと同様。
// コメント |
単一行コメント | 行末までコメント |
/* コメント */ |
複数行コメント | 「*/」が来るまでコメント |
C言語やJavaと異なり、Scalaの複数行コメントはネストすることが出来る。[2010-12-07]
/* abc /* def */ ghi */
C言語やJavaだと、最初の「*/」が出てきた時点でコメント終了となる。
したがって上記の例では「ghi」という文を解釈しようとし、またその直後にいきなり「*/」があるのでコンパイルエラーになる。
Scalaなら問題ない。
逆に、Scalaでは「/*」と「*/」をきちんと対応させなければならない。
Javaでは、(複文以外の)文の末尾にはセミコロン「;
」を付ける必要がある。
Scalaでは、命令の末尾が文の末尾であることが明白(例えば改行で終わっている)な場合は、セミコロンを省略できる。
val a = 123; val b = 123
同一行に複数の文を書きたい場合はセミコロンで区切る。
val a = 123; val b = 456
Javaでは変数名などが長くて計算式が長くなる場合、途中で改行することがよくある。[2010-11-28]
Scalaでは改行すると文が終わったと解釈されてコンパイルが通ってしまう場合があるので、注意が必要。
例 | f(2,3)の結果 | 解説 | |
---|---|---|---|
Java |
int f(int a, int b) {
final int r = a + b
+ 1;
return r;
}
|
6 | 「a + b + 1 」の途中で改行を入れても大丈夫。セミコロンが来るまでは一つの文と解釈される。 |
Scala (文が続かない例) |
def f(a:Int, b:Int): Int = { val r = a + b + 1; return r; } |
5 | 「val r = a + b 」で文が終わっていると解釈される。次の「 + 1; 」は、結果をどこにも返さない文になる。(コンパイルエラーにはならない)これが「 * 2; 」であれば、コンパイルエラーになる。つまり「+1」は正符号つきの整数として正しいから通る。 |
def f(a:Int, b:Int): Int = { a + b + 1; } |
1 | returnを省略した場合は一番末尾の式が戻り値となる。 (Scalaではreturnを省略するのが一般的) 「 a + b 」と「+ 1 」は別の式という解釈なので、後の式の演算結果である1が返る。 |
|
Scala (文が続く例) |
def f(a:Int, b:Int): Int = { val r = a + b + 1; return r; } |
6 | 「val r = a + b + 」は「+」で終わっているので、文が続いていると解釈される。 |
def f(a:Int, b:Int): Int = { a + b + 1; } |
6 | ||
scala> 1 + scala> "abc" + |
直接REPL上で式を書く場合は「+」で終わっていてもエラーになる。[2011-03-05] |
Scalaでも、とげ括弧で複数の文を囲った「複文」を置くことが出来る。[2010-11-28]
複文と言うのか複式と言うのか…と思っていたら、「ブロック式」と呼ぶらしい。[/2011-01-08]
{ 式; … } { 式 … } { 式 … }
そのブロック式の値は、ブロック式内の一番末尾の式の値となる。
scala> val r = { 1+2; 3+4; 5+6 } r: Int = 11
Scalaでは、「変数名:
型」によって変数を宣言する。
(変数名の後ろにコロン「:
」で区切って型を書く。C言語やJavaでは型の後ろに変数名を書く)
宣言箇所 | 宣言方法 | 備考 |
---|---|---|
変数 | var 変数名 = 初期値 |
(後から値の代入・変更が出来る)変数は、「var」によって宣言する。 初期値の型と同じでいい場合は、型の宣言は省略可能。 |
定数 | val 変数名 = 初期値 |
定数(後から値の代入をすることが出来ない変数)は、「val」によって宣言する。 (Javaのfinal変数に相当) 初期値によって型が分かるので、型は省略可能。 |
メソッド・関数の引数 | def 関数名(変数名: 型, …) |
関数やメソッドの引数の場合、型は省略不可。 |
varはvariable(変数)、valはvalue(値、すなわち不変)という事かな?
varでは、初期値に「_」を書くと、その型のデフォルト値を指定することが出来る。[2010-11-28]
(デフォルト値は、0やnull(Booleanはfalse))
scala> var v:Int = _ v: Int = 0
val・varでは、複数の変数をいっぺんに初期化することが出来る。[2011-01-08]
scala> val a, b, c = 1
a: Int = 1
b: Int = 1
c: Int = 1
newによるインスタンス生成を書くと、別々のインスタンスが生成される。
scala> val a, b, c = new Object
a: java.lang.Object = java.lang.Object@c6c084
b: java.lang.Object = java.lang.Object@893e69
c: java.lang.Object = java.lang.Object@607135
関数呼び出しにすれば、変数の個数分呼び出される。
scala> var n = 0
n: Int = 0
scala> val a,b,c = { n += 1; println("called" + n); n }
called1
called2
called3
a: Int = 1
b: Int = 2
c: Int = 3
要するに、個別に初期化を書くのをまとめられるという糖衣構文らしい。
変数宣言時にもパターンマッチを使用することが出来る。[2011-02-22]
scala> val (a,b) = (123, 456) //タプルの要素を変数にする a: Int = 123 b: Int = 456 scala> val Array(a0, a1, _*) = Array(1,2,3,4,5) //配列の要素を変数にする a0: Int = 1 a1: Int = 2
「@」を使うと、個々の要素と全体を同時に変数にすることが出来る。
scala> val t@(a,b) = (123, 456) t: (Int, Int) = (123,456) a: Int = 123 b: Int = 456 scala> val a @ Array(a0,a1, _*) = Array(1,2,3,4,5) a: Array[Int] = Array(1, 2, 3, 4, 5) a0: Int = 1 a1: Int = 2 scala> val a @ Array(a0,a1, other @ _*) = Array(1,2,3,4,5) a: Array[Int] = Array(1, 2, 3, 4, 5) a0: Int = 1 a1: Int = 2 other: Seq[Int] = Vector(3, 4, 5)
valに「lazy」を付けると、遅延評価されるようになる。[2011-01-08]
その定数が初めて参照されるまで計算されず、一度計算されると再度計算されることは無い。
scala> val n1 = 1; val n2 = 2 n1: Int = 1 n2: Int = 2 scala> lazy val v = { println("sum"); n1 + n2 } v: Int = <lazy> scala> v sum //初めて参照するときに計算される res0: Int = 3 scala> v //二度目以降は計算されず、最初に計算された値が返る res1: Int = 3
なお、「lazy var」は出来ない。
条件に応じてどちらかの文を実行する。[2010-11-28]
if式は値を返すので、JavaやC言語の三項演算子(条件演算子)「b ?
式1 :
式2」の方が近いかも。
if (条件式) 式1 if (条件式) 式1 else 式2 if (条件式) { 式;… } if (条件式) { 式;… } else { 式;… }
条件式にはBooleanの値しか使えない。
if式の値は、条件式がtrueの場合は式1の値になる。
falseの場合は式2の値となるが、elseが省略されている場合は(Unitの値である)「()」になる。
scala> if (true) 1 else 2 res5: Int = 1 scala> if (false) 1 else 2 res6: Int = 2 scala> if (true) 1 res7: AnyVal = 1 scala> if (false) 1 res8: AnyVal = () scala> val r = if (true) println(123) 123 r: Unit = ()
式1と式2の型が異なる場合、if式の戻り型は両者共通の型(大きい方)になる。
scala> if(true) 1 else 2L res16: Long = 1
Scalaでは、加減算などの演算の方がif式よりも優先順位が高い。
したがって、以下のような書き方をすると誤解を招くことがあるかもしれない。
scala> if(true){ 1 }else{ 2 } + 3 res13: Int = 1 scala> (if(true){ 1 }else{ 2 }) + 3 res14: Int = 4 scala> if(true){ 1 }else({ 2 } + 3) res15: Int = 1
一番上の例では、「{ 2 } + 3」の優先順位が高い。つまり「+ 3」の部分は、else部にしか影響していない。
まぁ、Javaの三項演算子と同様だと思えば、そんなに違和感は無いかも。
(要するに、誤解を招きそうな箇所では括弧を付けろ、と)
int r = (true) ? 1 : 2 + 3 int r = (true) ? (1) : (2) + 3 int r = ((true) ? (1) : (2)) + 3 int r = (true) ? (1) :((2) + 3)
条件が満たされている間、処理を繰り返す。[2010-11-28]
while (繰り返し条件) 式 while (繰り返し条件) { 式;… }
条件式にはBooleanの値しか使えない。
while式には戻り値は無い(戻り型がUnit)。
breakやcontinueは無い。(→Scala2.8のbreak)
条件が満たされている間、処理を繰り返す。必ず一度は実行される。[2010-11-28]
do 式 while (繰り返し条件) do { 式;… } while (繰り返し条件)
条件式にはBooleanの値しか使えない。
do式には戻り値は無い(戻り型がUnit)。
breakやcontinueは無い。(→Scala2.8のbreak)
for (ジェネレータ) 式
→for式
Scala2.8ではbreakを使うことが出来る。[2010-11-28]
import scala.util.control.Breaks.{break, breakable} //import scala.util.control.Breaks._ object BreakExample { def main(args: Array[String]) { var n = 0 breakable { while(true) { println(n) n += 1 if (n >= 5) break } } } }
しかしこれ、ソースを見るとbreak関数の中でBreakControlという例外をスローしてbreakableでキャッチするようになっているので、「正常処理の分岐に例外を使用している」という意味で、あんまり良くないような気がする(苦笑)
こういう実装方法なら、そりゃcontinueは作れないよなぁ…。(特にdoやfor用)
しかしScalaにはControlThrowableというクラス(BreakControlもその派生クラス)があるようなので、フロー制御でこういった例外を使うのはScalaでは許されるのかな?
値 match { case 条件1 => 式1 case 条件2 => 式2 … }
例外を捕捉する。
try { 式 … } catch { case 変数:例外クラス => 式1 … } finally { 式 }
例外を発生させる(投げる)。
関数・メソッドから値を返す。