羽生田栄一(はにゅうだ えいいち)
株式会社豆蔵 取締役・プロフェッショナルフェロー
 モデリングやソフトウエア工学の実践適用に関するコンサル・教育を通じて後進の育成にあたる。現在,アジャイルプロセス協議会会長,SEC要求設計技術部会委員,パターンワーキンググループ主査,情報処理学会ソフトウェア工学研究会主査,IPA ITアーキテクト・コミュニティ委員などを務める。路地・トマソン物件・神社・富士塚・古書店等を巡る街歩きが趣味。オブジェクト指向技術関連の著書・訳書多数。技術士(情報工学部門)。

 今回は,強力な純粋オブジェクト指向言語でありベターJavaとして有用なScala言語の部分ではなく,関数型言語としてのScalaを紹介します。関数自体をオブジェクトとして変数の値にしたり,引数として別のメソッドに渡したりという関数の“第1級市民”としての取り扱いを見ていきましょう。また,関数型言語の特徴であるパターンマッチングがいかに強力かも体験してもらいます。ではさっそく,Scalaミニツアー第3弾の探検に出発しましょう。

 今回,日本語文字列を使うので,Scala対話環境で試される場合には,「scala -Xnojline」としてコマンドラインから立ち上げてください。

Scalaの関数定義

 Scalaでは,メソッドは関数としてdefを用いて,「 def 関数名(引数リスト): 戻り値型 = 関数本体 」として定義します。

def foo(s: String, n: Int): Int = {
  s.length * n
}

 ただし,関数本体が式1個からなる場合は,{ }を省略できます。

def foo(s: String, n: Int): Int = s.length * n

 さらに,戻り値型は型推論が働くので,省略することも可能です(これから定義しようとしている関数の引数なので当然ですが,引数型は省略できません)。ただし,対話環境でスクリプト言語的に使う場合以外は,理解を容易にするためにはシグニチャとして残しておくほうがよいでしょう。

scala> def foo(s: String, n: Int) = s.length * n
foo: (String,Int)Int
scala> foo("豆蔵太郎", 3)
res0: Int = 12

 戻り値のないメソッドを宣言する場合には,戻り値型としてUnit型を指定します。これはJavaでいうvoidに相当します。

def bar(s: String, n: Int): Unit = for(i <- 1 to n) print(s)

 この関数は,指定された文字列をn回印字するという副作用を実行するためのものなので,戻り値型をUnitにしておきます。ちなみにUnit型のユニークなインスタンスは,形式的に( )というリテラルで表されます。

シングルトンオブジェクト内のメソッドのimport

 これらのメソッド定義は,通常はクラス定義の中で利用するのですが,関数として単独で利用したい場合は,シングルトンオブジェクト内に定義するのが普通です。

object MyFunctions {
  def foo(s: String, n: Int): Int = s.length * n
  def bar(s: String, n: Int): Unit = for(i <- 1 to n) print(s)
}

 これらのメソッドfooやbarを利用するには,普通はこれらの定義されているオブジェクト名を一緒に指定して呼び出します。

MyFunctions.foo("豆蔵太郎", 3)   12
MyFunctions.bar("豆蔵太郎", 3)   豆蔵太郎豆蔵太郎豆蔵太郎

 オブジェクト名をいちいち指定したくなければ,次のようにimportをしてしまえば簡単です。これでMyFunctions内のすべての定義がインポートされます。

scala> import MyFunctions._
scala> foo("豆蔵太郎", 2)
res1: Int = 8
scala> bar("豆蔵太郎", 2)
豆蔵太郎豆蔵太郎