M.Hiroi's Home Page

Scala Programming

お気楽 Scala プログラミング入門

[ PrevPage | Scala | NextPage ]

Scala の基礎知識

●使ってみよう

それでは、さっそく Scala を使ってみましょう。シェルでコマンド scala を実行すると、対話モード (REPL : Read-Eval-Print-Loop) で Scala を起動することができます。

$ scala
Welcome to Scala 2.13.5 (OpenJDK 64-Bit Server VM, Java 11.0.10).
Type in expressions for evaluation. Or try :help.

scala>

scala> は Scala のプロンプトです。終了するには Ctrl-D (Ctrl キーを押しながら D を押す)を入力する、またはコマンド :quit (:q でもよい) を入力してください。プロンプトのあとに式を入力すると、Scala は式を実行して結果を返します。

scala> 1 + 2 * 3
val res0: Int = 7

scala> 2 - 3
val res1: Int = -1

scala> 1.1 + 2.2
val res2: Double = 3.3000000000000003

scala> res0
val res3: Int = 7

scala> res1
val res4: Int = -1

scala> res2
val res5: Double = 3.3000000000000003

res0, res1, ..., res5 は対話モードで入力された式の実行結果を受け取る変数です。Scala は式 1 + 2 * 3 の計算結果を変数 res0 に格納して値を表示します。結果を見ると、値が 7 でデータの種類が Int (整数) であると表示されます。1.1 + 2.2 を計算すると、結果が 3.3 でデータの種類が Double (浮動小数点数) であることが表示されます。

●整数と浮動小数点数

データの種類や種別のことを「データ型」、またはたんに「型」といいます。Scala は標準で整数 (integer) と浮動小数点数 (floating point number) を使うことができます。下表に整数と浮動小数点数の種類を示します。

表 : 整数と浮動小数点数
型名範囲
Byte-128 ~ 127 (8 bit)
Short-32768 ~ 32767 (16 bit)
Int-2147483648 ~ 2147483647 (32 bit)
Long-9223372036854775808 ~ 9223372036854775807 (64 bit)
Float±1.1754944E-38 ~ 3.4028235E+38 (32 bit 単精度)
Double±2.22507E-308 ~ 1.79769E+308 (64 bit 倍精度)

数字を並べて書くと Int になります。整数の末尾に L または l を付けると Long になります。数字の先頭に 0x を付けると 16 進数になります。小数点付きの数字は Double になります。数字の末尾に D または d を付けても Double になります。F または f を付けると Float になります。もちろん、型を指定することもできます。型指定は数字の後ろに :型 を付けます。

このほかにも、多倍長整数を表す BigInt や任意精度の符号付き 10 進数を表す BigDecimal があります。

簡単な例を示しましょう。

scala> 12345
val res6: Int = 12345

scala> 12345L
val res7: Long = 12345

scala> 12345.6789
val res8: Double = 12345.6789

scala> 12345.6789F
val res9: Float = 12345.679

scala> 125:Byte
val res10: Byte = 125

scala> 12345:Short
val res11: Short = 12345

●算術演算子、比較演算子、論理演算子

Scala の算術演算子、比較演算子、論理演算子を下表に示します。

表 : 算術演算子
演算子操作
-x x の符号を反転
x + y x と y の和
x - y x と y の差
x * y x と y の積
x / y x と y の商
x % y x と y の剰余

表 : 比較演算子
演算子意味
== 等しい
!= 等しくない
< より小さい
> より大きい
<= より小さいか等しい
>= より大きいか等しい

表 : 論理演算子
操作意味
!x x の否定(真偽の反転)
x && y x が真かつ y が真ならば真
x || y x が真まはた y が真ならば真

これらの演算子は Java とほとんど同じです。Scala は Java と同様に真偽値を表す型 Boolean が用意されています。true が真で、false が偽を表します。論理演算子の場合、C/C++とは違って引数は Boolean 型のデータでなければいけません。&& は左項が偽ならば右項を実行せずに偽を返します。|| は左項が真ならば右項を実行せずに左項の値を返します。このため、&& と || は「短絡演算子」と呼ばれることもあります。

簡単な使用例を示します。

scala> 1 + 2 * 3 - 4
val res12: Int = 3

scala> 4 / 3
val res13: Int = 1

scala> 4.0 / 3.0
val res14: Double = 1.3333333333333333

scala> 11 % 4
val res15: Int = 3

scala> 1 < 2
val res16: Boolean = true

scala> 1 == 1
val res17: Boolean = true

scala> 1 != 1
val res18: Boolean = false

scala> 1 > 2
val res19: Boolean = false

scala> !true
val res20: Boolean = false

scala> true && true
val res21: Boolean = true

scala> true && false
val res22: Boolean = false

scala> true || false
val res23: Boolean = true

scala> false || false
val res24: Boolean = false

算術演算で右辺と左辺の型が異なる場合、結果は大きな範囲の型になります。ただし、Byte と Short の演算結果は Int になります。整数と浮動小数点数の演算結果は浮動小数点数になりますが、多倍長整数 (BigInt) と浮動小数点数の演算はエラーになります。

簡単な例を示します。

scala> (1:Short) + (2:Short)
val res25: Int = 3

scala> (1:Short) + (2:Int)
val res26: Int = 3

scala> (1:Long) + (2:Int)
val res27: Long = 3

scala> (1:Long) + (2:BigInt)
val res28: scala.math.BigInt = 3

scala> (1:Long) + (2:Float)
val res29: Float = 3.0

scala> (1:Long) + (2:Double)
val res30: Double = 3.0

scala> (1:BigInt) + (2:Double)
                      ^
       error: type mismatch;
        found   : Double
        required: scala.math.BigInt

●文字と文字列

Scala には文字を表す型 Char が用意されています。Java の char と同じく unicode で、シングルクオート ( ' ) で囲んで表します。数値で記述する場合は '\uXXXX' のように 4 桁の 16 進数を使います。文字列 (String) はダブルクオート " で囲んで表します。これは Java の文字列とほとんど同じです。

簡単な例を示します。

scala> 'A'
val res32: Char = A

scala> "hello, world"
val res33: String = hello, world

scala> "abcd" + "efgh"
val res34: String = abcdefgh

scala> 1 + "1"
         ^
       warning: method + in class Int is deprecated (since 2.13.0): 
       Adding a number and a String is deprecated. Use the string interpolation `s"$num$str"`
val res35: String = 11

文字列は演算子 + を使って連結することができます。この場合、"abcd" と "efgh" を連結した新しい文字列 "abcdefgh" が生成されます。演算子 + は文字列を連結するとき、左右どちらかのデータが文字列ではない場合、そのデータを文字列に変換してから連結 [*1] します。たとえば、1 + 1 は整数の足し算で値は 2 になりますが、1 + "1" は 1 を文字列に変換して連結するので "11" になります。

また、文字列には「エスケープシーケンス」を含めることができます。これは、画面に表示することができない文字を表すのに用いられる方法です。よく使われる記号に改行を表す \n とタブを表す \t があります。

もうひとつ、文字列の定義には三重のダブルクオート """ で囲む方法 [*2] があります。これを raw string といいます。次の例を見てください。

scala> """abcd\nefgh"""
val res36: String = abcd\nefgh

scala> "abcd\nefgh"
val res37: String =
abcd
efgh

scala> """abcd
     | efgh"""
val res38: String =
abcd
efgh

""" で囲まれた入力データがそのまま文字列になります。\n は改行文字として扱われるのではなく、文字そのままの \ と n になります。また、文字列を途中で改行すれば、その改行文字を含む文字列が生成されます。

-- note --------
[*1] この機能は ver 2.13.0 から非推奨になりました。代わりに「文字列の補完」という機能を使います。たとえば、文字列 s" ... " の中で ${ expr } は式 expr を評価した値に置き換わります。expr が変数名の場合は { } を省略することができます。Perl の変数展開や Ruby の式展開などと同様の機能です。
[*2] Perl や UNIX 系のシェルにあるヒアドキュメント (here-document) という機能とよく似ている方法です。

●変数

変数 (variable) は val または var で宣言します。

val 変数名: 型 = 初期値
var 変数名: 型 = 初期値

val または var のあとに変数を表す名前を書きます。その後に型を指定します。型は Scala の「型推論」により省略することができます。変数を定義するときは初期値を指定します。これを変数の初期化といいます。また、変数に値をセットすることを「代入」といい、代入には = を使います。

ここで重要なのが val と var の違いです。val で宣言された変数は値を書き換えることができません。これを immutable といいます。これに対し、var で宣言された変数は値を書き換えることができます。これを mutable といいます。

純粋な関数型言語の場合、変数は immutable なので、値を書き換えることはできません。手続き型言語では、変数を間違った値で書き換えるとプログラムは正常に動作しなくなります。プログラムの規模が大きくなるほど、このような間違い(バグ)を見つけるのは難しくなります。純粋な関数型言語であれば、このようなバグに悩まされることはありません。プログラムが正しく動作するか検証するのも手続き型言語より容易になります。Scala で関数型プログラミングを行うときは val を使うようにしてください。

簡単な実行例を示します。

scala> val a = 1
val a: Int = 1

scala> val b = 1.234
val b: Double = 1.234

scala> val c = "hello, world"
val c: String = hello, world

scala> a = 2
         ^
       error: reassignment to val

scala> var d = 12345
var d: Int = 12345

scala> d = 123456789
// mutated d

scala> val n1, n2, n3 = 100
val n1: Int = 100
val n2: Int = 100
val n3: Int = 100

val で宣言した変数の値を書きようとするとエラーになります。var で宣言した変数は値を書き換えることができます。また、最後の例のように、複数の変数を同時に定義することもできます。

●条件分岐

条件分岐は if を使います。Java の if 文と違って Scala の if は「式」であることに注意してください。つまり、if は値を返すことができるのです。if の構文を示します。

if (条件式) 式A else 式B

if の後ろのカッコの中に条件式を書きます。条件式の実行結果が真であれば、式A を実行します。偽の場合は else の後ろに書かれている 式B を実行します。真のときに実行する処理を then 節、偽のときに実行する処理を else 節といいます。

簡単な使用例を示します。

scala> if (true) 1 else 0
val res39: Int = 1

scala> if (false) 1 else 0
val res40: Int = 0

scala> if (1 < 2) 1 else -1
val res41: Int = 1

scala> if (1 > 2) 1 else -1
val res42: Int = -1

複数の処理を実行したい場合は { } で囲んで記述します。これを「ブロック」といいます。Scala の場合、ブロックも「式」になります。ブロックは最後に実行した式の結果を返します。

scala> if (true) {
     | println("foo!")
     | 1
     | } else {
     | println("bar!")
     | 0
     | }
foo!
val res43: Int = 1

scala> if (false) { println("foo!"); 1 } else { println("bar!"); 0 }
bar!
val res44: Int = 0

1 行に複数の式を記述したい場合はセミコロン ( ; ) で区切ってください。println はデータを画面に表示する関数です。

もう少し複雑な使い方を紹介しましょう。

if (test_a) {
    処理A
} else {
    if (test_b) {
        処理B
    } else {
        処理C
    }
}

図 : if 文の入れ子

           図 : if 文の入れ子の動作

test_a が偽の場合は else 節を実行します。else 節は if 文なので、条件 test_b を実行します。この結果が真であれば処理 B を実行します。そうでなければ、else 節の処理 C を実行します。この処理は下図のように書き換えることができます。

if (test_a) {
    処理A
} else if (test_b) {
    処理B
} else {
    処理C
}

図 : if の構文 (2)

Java と同様に eles if を使って if 文を連結することができます。test_a が偽の場合は、次の else if の条件 test_b を実行します。この結果が真であれば処理 B を実行します。そうでなければ、else 節の処理 C を実行します。なお、else if はいくつでも繋げることができます。

if 式の else 節は、不要であれば省略することができます。else 節を省略すると、条件式の結果が偽のときは Unit 型のデータが返されます。Unit 型のデータは () しかありません。次の例を見てください。

scala> val u = println("hello, world")
hello, world
val u: Unit = ()

println は画面にデータを出力する「副作用 (side effect)」が目的の関数です。hello, world は出力結果であり、println の返り値ではありません。() が println の返り値です。Unit はこのような副作用が目的の関数の返り値として使われます。

then 節と else 節の返り値の型が異なる場合、Scala は両者に共通の型を推論します。

scala> val a1 = if (true) 1 else 1L
val a1: Long = 1

scala> val b1 = if (true) 1 else 1.0
val b1: Double = 1.0

scala> val c1 = if (true) 1
val c1: AnyVal = 1

scala> val d1:Int = if (true) 1
                    ^
       error: type mismatch;
        found   : Unit
        required: Int

Int と Long であれば Long に、Int と Double であれば Double に、Int と Unit であれば AnyVal [*3] という型に推論 [*4] されます。変数 d の型を Int に指定すると、if (true) 1 はエラーになります。if の返り値を捨てる場合は問題ありませんが、そうでない場合は注意してください。

-- note --------
[*3] Scala の場合、型はクラスのことで、AnyVal は Int と Unit の共通のスーパークラスと考えてください。
[*4] ちなみに、関数型言語 SML/NJ や OCaml の場合、then 節と else 節の型が異なるとコンパイルエラーになります。

●while による繰り返し

繰り返しは同じ処理を何度も実行することです。まずは簡単な繰り返しから紹介しましょう。while は条件が真のあいだ、ブロックに書かれている処理を繰り返し実行します。ちなみに、Scala の while は式で、返り値は Unit です。

while (test) {
  処理A
  処理B
  処理C
}

図 : while 式の構文

       図 : while 式の動作

上図を見ればおわかりのように、while 式はいたって単純です。この動作は Java の while 文とほぼ同じです。

簡単な例を示しましょう。hello, world を 10 回表示します。

scala> var n = 0
var n: Int = 0

scala> while(n < 10){ println("hello, world"); n += 1 }
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world

変数 n を 0 に初期化し、n の値が 10 よりも小さいあいだ処理を繰り返します。Java と同様に、n += 1 は n = n + 1 と同じ意味です。このほかに、-=, *=, /=, %= などがあります。ただし、Scala には演算子 ++ と -- はありません。n の値はブロックを実行するたびに +1 されていくので、n が 10 になった時点で繰り返しを終了します。

while 文には do { ... } while(test); という形式もあります。これはブロックの処理を実行してから条件部をチェックします。したがって、ブロックの処理は最低でも 1 回は必ず実行されます。プログラムを書き直すと、次のようになります。

scala> n = 0
// mutated n

scala> do {println("hello, world"); n += 1} while (n < 10)
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world
hello, world

これでも hello, world を 10 回表示することができます。

●for による繰り返し

回数を指定して処理を繰り返す場合、while よりも for を使ったほうが簡単です。Scala の場合、for も式で返り値は Unit です。

1. for (変数 <- i to j by k) 式
2. for (変数 <- i until j by k) 式

1 の i to j by k は i 以上 j 以下の数値を増分 k で生成して、順番に変数に代入します。by を省略すると増分は 1 になります。2 の i until j by k は i 以上 j 未満の数値を増分 k で生成して、順番に変数に代入します。

簡単な実行例を示します。

scala> for(n <- 1 to 10) println(n)
1
2
3
4
5
6
7
8
9
10

scala> for(n <- 0 until 10 by 2) println(n)
0
2
4
6
8

Scala の for 式は Java の拡張 for 文の動作と同じです。for 式の構文と動作を下図に示します。

for (変数 <- コレクション) { 処理A; ...; 処理Z; }

          図 : for 式の処理

一般に、複数の要素を格納するデータ型を「コレクション (collection)」とか「コンテナ (container)」と呼びます。Scala のライブラリには Array や List など便利なコレクションが用意されていて、それらにも for 式を使用することができます。i to j by k. i until j by k は Range という数値を順番に並べたコレクションを生成します。i until j by k は Range(i, j, k) と同じ動作になります。

簡単な実行例を示します。

scala> 1 until 10
val res49: scala.collection.immutable.Range = Range 1 until 10

scala> 1 until 10 by 2
val res50: scala.collection.immutable.Range = inexact Range 1 until 10 by 2

scala> Range(1, 10)
val res51: scala.collection.immutable.Range.Exclusive = Range 1 until 10

scala> Range(1, 10, 2)
val res52: scala.collection.immutable.Range.Exclusive = inexact Range 1 until 10 by 2

scala> for (i <- Range(1, 10)) println(i)
1
2
3
4
5
6
7
8
9

●FizzBuzz 問題

それでは簡単な例題として、FizzBuzz 問題を Scala で解いてみましょう。FizzBuzz 問題は 1 から 100 までの値を表示するとき、3 の倍数のときは Fizz を、5 の倍数ときは Buzz を表示するというものです。FizzBuzz 問題の詳細については Fizz Buzz - Wikipedia をお読みください。

プログラムは次のようになります。

リスト : FizzBuzz 問題 (fizzbuzz.scala)

object FizzBuzz {
  def main(args: Array[String]): Unit = {
    for (n <- 1 to 100) {
      if (n % 3 == 0 && n % 5 == 0)  // n % 15 == 0 でもよい
        print("FizzBuzz")
      else if (n % 3 == 0)
        print("Fizz")
      else if (n % 5 == 0)
        print("Buzz")
      else
        print(n)
      print(" ")
    }
  }
}

ファイル名は fizzbuzz.scala とします。Scala のソースファイルの拡張子は scala です。Scala はオブジェクト指向言語なので、クラスを定義しないとプログラムを作ることができません。ここで簡単にクラス、インスタンス、メソッドについて説明しておきましょう。

●オブジェクト指向の簡単な説明

クラスはオブジェクトの設計図にあたるもので、オブジェクトの「雛形」と呼ぶこともあります。このクラスから実体として作り出されるのがインスタンスです。このインスタンスを「オブジェクト」と考えてください。インスタンスを生成する方法は、当然ですがプログラミング言語によって違います。たとえばC++, Java, Scala は new を使います。

Scala の場合、メソッドはクラスの中で定義します。一般に、メソッドはインスタンスを操作するための関数で、メソッドを呼び出すにはインスタンスが必要になります。インスタンスはひとつのクラスから複数個を生成することができますが、Scala にはひとつのインスタンスしか生成することができないクラスがあります。これを「シングルトンオブジェクト」といって、object で定義します。objcet FizzBuzz { ... } はシングルトンオブジェクトで、初めてアクセスするとき自動的にインスタンスがひとつだけ生成されます。

main はシングルトンオブジェクトのメソッドで、C/C++の main 関数と同様にプログラムの実行は main メソッドから始まります。main の引数 args にはコマンドラインで与えられた引数が格納されています。print や println はデータを標準出力 (画面) へ出力するメソッドで、シングルトンオブジェクト Predef に定義されています。Predef は暗黙のうちにインポート (import) されているので、オブジェクト名 Predef を付けなくても println だけで呼び出すことができます。

オブジェクト指向についてはあとで詳しく説明しますが、とりあえずプログラムを作るにはシングルトンオブジェクトと main メソッドが必要である、ということを覚えておいてください。

●実行結果

プログラムの説明に戻ります。for 文の中で変数 n を 1 から順番に 1 ずつ増やしていきます。最初の if で、n が 3 の倍数でかつ 5 の倍数かチェックします。この処理は 15 の倍数をチェックすることと同じなので、条件式を n % 15 == 0 としてもかまいません。そうでなければ、次の else if で n が 3 の倍数かチェックし、次の else if で n が 5 の倍数かチェックします。どの条件も該当しない場合は最後の else 節で n をそのまま出力します。

それでは実行してみましょう。

$ scalac fizzbuzz.scala
$ scala FizzBuzz
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 
22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 
41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 
FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77
Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 
97 98 Fizz Buzz

正常に動作していますね。

●素因数分解

もうひとつ、簡単な例を示しましょう。素因数分解とは、素数でない整数 (合成数) を素数の積の形に書き表すことです。たとえば、12 は 2 * 2 * 3 と素因数分解することができます。素因数分解は素数 2, 3, 5, ... で順番に割り算していけばいいのですが、いちいち素数を求めるのは大変なので、2 と 3 以上の奇数列で割り算していくことにします。

プログラムは次のようになります。

リスト : 素因数分解

object Factor {
  def main(args: Array[String]): Unit = {
    var n = 1234567890
    // 2 で割り算する
    while (n % 2 == 0) {
      print("2 ")
      n /= 2
    }
    // 奇数で割り算する
    var m = 3
    while (m * m <= n) {
      while (n % m == 0) {
        print(m); print(" ")   // print(s"$m ") でもよい
        n /= m
      }
      m += 2
    }
    if (n > 1) print(n)
  }
}

コメントは Java と同様に、/* と */ で囲むか // から行末までになります。なお、Scala は文字コードに標準で UTF-8 を使用します。UTF-8 以外の文字コードを使うと、コメントでもコンパイルエラーになります。プログラムは UTF-8 で記述するか、コンパイラ (scalac) のオプション (-encoding <encoding>) で文字コードを指定してください。たとえば、シフト JIS であれば -encoding Shift_JIS とします。

最初に 2 で割り算します。それから、奇数で割り算していきます。割り算するときは、その数で割り切れるあいだは割り算を続けることに注意してください。たとえば、27 を素因数分解すると 3 * 3 * 3 になりますが、3 を一回だけしか割り算しないと、結果は 3 * 9 のように素数ではない数が含まれてしまいます。

実行結果は次のようになります。

$ scalac factor.scala
$ scala Factor
2 3 3 5 3607 3803

どの数も素数で、2 * 3 * 3 * 5 * 3607 * 3803 を計算すると 1234567890 になります。なお、これはとても単純なアルゴリズムなので、大きな整数の素因数分解には適していません。巨大な合成数の素因数分解はとても難しい問題です。興味のある方は素因数分解について調べてみてください。

●数値積分

最後に数値積分で円周率 \(\pi\) を求めてみましょう。区間 [a, b] の定積分 \(\int_a^b f(x) \, dx\) を数値的に求めるには、区間を細分して小区間の面積を求めて足し上げます。小区間の面積を求める一番簡単な方法は長方形で近似することです。この場合、3 つの方法が考えられます。

  1. (b - a) * f(a)
  2. (b - a) * f(b)
  3. (b - a) * f((a + b) / 2)

1 は左端の値 f(a) を、2 は右端の値 f(b) を、3 は中間点の値 f((a + b) / 2) を使って長方形の面積を計算します。この中で 3 番目の方法が一番精度が高く、これを「中点則」といいます。このほかに、台形で近似する「台形則」や、2 次近似で精度を上げる「シンプソン則」という方法があります。

それでは実際に、中点則で \(\pi\) の値を求めてみましょう。\(\pi\) は次の式で求めることができます。

\( \pi = \displaystyle \int_0^1 \dfrac{4}{1 + x^2} \, dx \)

プログラムは次のようになります。

リスト : 数値積分で円周率を求める

object Midpoint {
  def main(args: Array[String]): Unit = {
    val n = 10000
    val w = 1.0 / n
    var s = 0.0
    for (i <- 1 to n) {
      val x = (i - 0.5) * w
      s += 4.0 / (1.0 + x * x)
    }
    println(s * w)
  }
}

変数 n が分割数です。最初に小区間の幅を求めて変数 w にセットします。面積は変数 s にセットします。次の for ループで区間 [0, 1] を n 個に分割して面積を求めます。

最初に x 座標を計算します。中間点を求めるため、変数 i を 1 から始めて、x 座標を次の式で求めます。

val x = (i - 0.5) * w

たとえば、変数 i が 1 の場合は 0.5 になるので、x は区間 [0 * w, 1 * w] の中間点になります。あとは、4 / (1 + x * x) を計算して s に加算します。最後に s に w を掛け算して全体の面積を求めます。

実行結果を示します。

$ scalac midpoint.scala
$ scala Midpoint
3.141592654423134

今回はここまでです。次回は「関数」と「配列」について説明します。


初版 2014 年 7 月 20 日
改訂 2021 年 3 月 7 日

Copyright (C) 2014-2021 Makoto Hiroi
All rights reserved.

[ PrevPage | Scala | NextPage ]