Hello Worldを嚙み砕こう

2018年8月14日(火)
伊藤 竜一
【Scalaはこわくない!マルチパラダイム言語Scalaの入門書!】株式会社インプレスR&Dより発行された「Scalaをはじめよう! ─マルチパラダイム言語への招待─」の立ち読みコーナー第3回です。

Hello Worldを嚙み砕こう

エントリポイント・メソッド・式と文・ブロック式

Hello World ― エントリポイント

 まず先ほどsbtによって生成されたsrc/main/scala/example/ScalaTour.scalaを見ていきましょう.Scastieであれば表示されるエディタを以下に置き換え実行してください.

リスト3.1: Hello World!(※sbtプロジェクト/ScastieWorksheetオフで実行)

 1: // これは一行コメントです
 2: /* これは複数行コメントです
 3: JavaやCと同じですね */
 4: 
 5: // 名前空間の宣言です.多くの場合ディレクトリ階層と合わせます
 6: package example
 7: 
 8: object ScalaTour {
 9:   def main(args: Array[String]): Unit = {
10:     val message = "Hello World"
11:     println(message) // 末尾改行付き標準出力
12:     /* 出力
13:     Hello World
14:     */
15:   }
16: }

 Hello Worldと出力されたら成功です.このmainというメソッドが自動的にエントリポイントとして選択され,プログラムが実行されました.

 今回はmainメソッドをエントリポイントとして利用していますが,Appトレイトというものでエントリポイントを実現することもできます.以下のようにすることでリスト3.1と等価なプログラムになります.

リスト3.2: Appトレイトを利用したエントリポイント

 1: package example
 2:
 3: object ScalaTour extends App {
 4:   val message = “Hello World”
 5:   println(message)
 6:   /* 出力
 7:   “Hello World”
 8:   */
 9: }

 本書ではより基本的であるmainメソッドを利用したサンプルで統一します.

Hello Worldの構成要素 ― メソッド・式と文・ブロック式

 まず一旦外側のobjectは後回しにして(5.2節で紹介します), mainメソッドの内側から見ていきましょう.

リスト3.3: 再代入不可能な変数の宣言

 1: val message = "Hello World"
 2: // message = "Changed World" // 再代入不可能

valというキーワードから始まっているこの文は再代入ができない不変な変数の宣言です.文末にセミコロン(;)は必要ありません.Javaの

リスト3.4: Javaでの再代入不可能な変数の宣言

final String message = "Hello World";

に相当します.Javaの場合は文字列であること示すStringを明示しなければなりませんが,Scalaは型推論の機能があるためこれを省略できます.なお,型が定まらない場合や明示的に書きたい場合は,以下のように型注釈をつけることもできます.

リスト3.5: 型注釈を明示した変数の宣言

 1: val message: String = "Hello World"

 また,以下のようにvalではなくvarというキーワードを使うことで再代入ができる可変な変数の宣言ができます.

リスト3.6: 再代入可能な変数の宣言

 1: var mutableMessage = "Hello World"
 2: mutableMessage = "Changed World" // 再代入可能

 もちろん,どちらも自由に利用できますが,基本的にはvalを利用すると良いでしょう.「ループするときのカウンタはどうするんだ?」といった疑問が湧くかもしれませんが,カウンタを利用せずループを実現するといった可変な値を利用しないで良い機能をScalaは提供しています.そのような機能や理由は追い追い紹介していきます.

 次にエントリポイントであるメインメソッドを参考に,Scalaにおけるメソッドを見ていきましょう.

リスト3.7: メソッド宣言の概形

def メソッド名(引数名: 引数の型,...): 返り値の型 = 式

 メソッドは以上の形で宣言できます.表記の順番などが多少異なりますが,他のプログラミング言語と大差がないことが解ると思います.ただし,ここでの部分に注意してください.メインメソッドの例ならば,

リスト3.8: ブロック式

 1: {
 2:   println("Hello World")
 3: }

に当たる部分です({}が含まれていることに注意してください).繰り返しになりますがこれはです.特に{}による式をブロック式と呼びます.ここで言うというのは,との対比で,は「評価のみを行って結果を返さない」一方,は「なんらかの計算が行われて結果の値を返すもの」を指します.例えば,先ほど扱った変数の宣言であるval message = "Hello World"messageという変数を作成するものの,結果を返すわけではないのでです.ブロック式がどのように式になっているのかというと,{}内の文や式が順番に評価され,{}内最後の式の結果がブロック式の結果として扱われます(他の言語に多いreturnキーワードは不要です)

 つまり今回の例では,println("Hello World!")が評価され,その結果であるUnit型の()がブロック式の返り値となります(printlnというメソッドは標準出力に出力するだけですが,返り値がないことを意味する()を返します.Unit型というのはJavaやCで言うvoidのようなものと考えておけばとりあえず大丈夫です).別の例として以下のようなコードを考えてみましょう.

リスト3.9: ブロック式と返り値

 1: val result = {
 2:   val x = 1 + 2
 3:   x * 2
 4: } // `result` に 6 が代入される

 この場合,まずval x = 1 + 2が評価され,次にx * 2が評価された結果(6)がブロック式全体の結果となって最終的にresultに代入されます.これらは更にブレークダウンしてみると以下のように見ることができます.

図3.1: 式と文のネスト

 点線で囲った部分が式を,実線で囲った部分が文を表します.式や文がネストして成り立っていることが分かると思います.さてここでメソッドの定義に戻ります.

リスト3.10: (再掲)メソッド宣言の概形

def メソッド名(引数名: 引数の型,...): 返り値の型 = 式

 見直してみると,メソッドの本体はであればなにが来ても良い,非常に自由度が高い形であることが分かります.例えば,メインメソッドの例とは違いブロック式ではない式を入れてみましょう.

リスト3.11: ブロック式ではない式によるメソッド①

 1: def add(x: Int,y: Int): Int = x + y

 このメソッドは引数xと引数yを加算した結果を返します.あたかも{}の省略した記法に見えますね.

リスト3.12: ブロック式ではない式によるメソッド②

 1: // 値をそのまま返すだけのメソッド
 2: def identity(x: Int): Int = x

といった書き方もできます.他の言語のように「括弧が必ず必要で,その中でいろいろな処理をしてreturnで返り値を指定する……」というわけではなく,とにかく式であればなんでもいいのです!


メソッド宣言の際に引数が0個の時は()ごと省略することもでき,省略して宣言されたメソッドの呼び出しには()が不要(むしろ付けるとコンパイルエラー)になります.なお,宣言時に省略していないときでも呼び出し時だけ省略することもできます.慣習的に,状態を変化させるメソッドの場合は()を付け,変化させない場合は()を省略することが多いです.本書ではわかりやすさのため,省略せずに表記しています.


【コラム】「???」を利用する

 まだコードが途中で不完全,でもちょっと動かして試してみたい……ということは多々あるかと思います.Scalaはコンパイラ言語なので,基本的にはきっちりしたコードを書くまで動かせません.ですが,やっぱり動かしたいですよね?特にインタープリタ言語から来た方にはむず痒いところでしょう.

 そこでScalaではデフォルトで???というものが利用できます.実態はNotImplementedError,つまり未実装を意味する例外なので実行フローに入れることはできませんが,本質的でない部分にとりあえず???を入れておくと実装(メソッドの本体,メソッドの返り値,etc.)を後回しにできます.

 例えば以下のようにすると,「barメソッドに奇数が渡されて呼び出される」・「bazメソッドが呼び出される」ことがないという前提で実装を省略したままコンパイル・実行できます.言い換えると「Fooクラスのbarメソッドに偶数が渡されて呼び出される」というシチュエーションのみ適切に実行できるということです.

リスト3.13: 未実装箇所のある実装例

 1: class Foo {
 2:   def bar(x: Int): Int = {
 3:     if (x % 2 == 0) x * 2 // 偶数であれば2倍して返す
 4:     else ??? // 奇数は未実装
 5:   }
 6: 
 7:   def baz(s: String): String = ??? // 未実装
 8: }

(次回へ続く)


大阪大学大学院情報科学研究科を修了後、さくらインターネット株式会社にてクラウドサービスのバックエンド開発に従事。プログラミング言語であるScala好きが高じ、Scala関西勉強会やScala関西Summitにて登壇。またその運営にも携わった。 最近はKubernetesと戯れている。

連載バックナンバー

開発ツール書籍・書評
第4回

FizzBuzzしてみよう

2018/8/16
【Scalaはこわくない!マルチパラダイム言語Scalaの入門書!】株式会社インプレスR&Dより発行された「Scalaをはじめよう! ─マルチパラダイム言語への招待─」の立ち読みコーナー第4回です。
システム開発書籍・書評
第3回

Hello Worldを嚙み砕こう

2018/8/14
【Scalaはこわくない!マルチパラダイム言語Scalaの入門書!】株式会社インプレスR&Dより発行された「Scalaをはじめよう! ─マルチパラダイム言語への招待─」の立ち読みコーナー第3回です。
開発言語書籍・書評
第2回

Scalaの環境を作る

2018/8/9
【Scalaはこわくない!マルチパラダイム言語Scalaの入門書!】株式会社インプレスR&Dより発行された「Scalaをはじめよう! ─マルチパラダイム言語への招待─」の立ち読みコーナー第2回です。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています