関数
ブロック
Scalaの関数には、無名関数やクロージャ、関数内での関数の定義など、関数型言語のエッセンスが取り込まれています。Javaユーザーにとっては新鮮であろうScalaのアプローチを理解するには、まず「ブロック」という処理単位(スクリプト言語「Ruby」のブロックのようなもの)を知る必要があります。
ブロックとは、中括弧{……}で囲まれた領域であり、最後に表現された値が戻り値となるものです。次にブロックの例を挙げます。
scala> { val x=1 ;val y=2; x+y}
res0: Int = 3
ブロック内で宣言された変数xと変数yを足した値が戻り値となっています(ここで、xとyがIntと型推論されたうえで、x+yもIntと型推論されています)。ブロック内で宣言された変数は、ブロック内でのみ有効です(すなわち、ブロックは変数のスコープの区切りとなっています)。そのため、ブロック外から直接変数xを参照するとエラーとなります。
scala> x <console>:5: error: not found: value x
ブロック内の最後の行が値を返さない場合には、Unit型という型を返します。
名前付きの関数を定義する
関数は次のように定義します。
def 関数名 : 戻り値の型 = { ブロック }
def 関数名(引数名:引数の型, 引数名:引数の型, ……) : 戻り値の型 = { ブロック }
ブロック内に関数の実体(処理内容)を記述します。戻り値の型は、変数の時と同じく省略可能です(自明なことは書かないというScalaの設計思想だと筆者は思っています)。
一方、引数名の型は省略できません。引数を明示させることで、コンパイル時のコードの最適化および型推論を可能としています。LIST4に引数ありの関数の例を挙げます。
なお、LIST4の縦棒(|)は、複数行にまたがる記述であることを示している記号で、実際に入力する必要はありません。
scala> def funcArg(last : String, first : String) : String = {
| println("hello! " + last + " " + first)
| last + first
| }
funcArg: (String,String)String
関数定義に「return」がないことが気になったかもしれません。returnを使う/使わないは自由ですが、{……}で区切られたブロックに、等号(=)で名前を付けることが関数定義という理解に立ち、returnのない記述にも慣れておくのがよいでしょう。
ブロックが関数定義の最後の行の値を返すためですが、この点は読みやすさを重視して、last+firstという部分をreturn(last+first)と書くこともできます。また、関数定義時に関数名の後に等号(=)を忘れてしまうと、コンパイルは通りますが、こちらの意図したとおりに動作しないので注意が必要です。
それでは、定義した関数funcArgを使ってみましょう。
scala> funcArg("yuuji","saito") hello! yuuji saito res3: String = yuujisaito
これは第1引数と、第2引数が表示され、戻り値として、2つの引数を結合したものが返ってきています。
関数内での関数の定義
Scalaでは、関数内に関数を定義できます(便宜上、これを親関数と子関数と言います)。その場合、親関数内で定義した子関数は、親関数の中からのみ呼び出すことができます(LIST5)。
scala> def func1(arg1:int, arg2:int) = {
| val func1valStart = "OK"
|
| def func2(arg3 : int) = {
| println(func1valStart)//関数の前に宣言された変数にはアクセスできる
| //println(func1valEnd)//関数の後に宣言された変数にはアクセスできない
| (arg1 + arg2) * arg3
| }
| val func1valEnd = "NG"
| func2(10)
| }
warning: there were deprecation warnings; re-run with -deprecation for details
func1: (int,int)Int
scala> val ret = func1(1,2)
OK
ret: Int = 30
scala> ret
res3: Int = 30
func1関数は、引数1と引数2を足して、さらに10倍して返す関数です。子関数は、親関数の引数や、子関数の定義前に宣言された変数へアクセスできるという特徴を持っています。今回は解説を省きますが、Scalaの関数は関数自体を引数にとれます。また、再帰的に処理する関数を容易に記述できます。これらについて興味のある方は、Scalaの解説書などを参考にしてください。
クラス
クラスの宣言方法はLIST6のとおりです。extendsキーワードで、親クラスを継承できます。また、withキーワードとしてtraitを使用することで機能拡張(mix-in型の多重継承)が可能となります。メンバーの初期アクセス権限はpublicです。アクセス修飾詞にはpublic、protected、privateが存在します。
class クラス名(メンバ変数1, メンバ変数2, ……) extends 親クラス with トレイト {
//メンバ変数(デフォルトでのスコープはpublicである)
val publicMemberVal1
protected val protectedMemberVal1
private val privateMemberVal1
//メンバ関数(デフォルトでのスコープはpublicである)
def publicFunc = {}
private def privateFunc = {}
}
オブジェクト(シングルトンオブジェクト)
Scalaは、オブジェクト指向でよく利用されるシングルトンオブジェクト(注3)を、言語仕様としてサポートしています。宣言方法は、classキーワードを使用せずに、objectキーワードを用いた宣言を行うことで(パッケージ内で)唯一のシングルトンオブジェクトが生成されます。シングルトンオブジェクトは引数を持てない点を除いてクラスと同じです(LIST7)。
シングルトンパターンとは、複数のクライアントにサービスを提供するといった目的のために、インスタンスが1つしか生成されないことを保証するデザインパターンです。
object オブジェクト名 {
//メンバ変数(デフォルトでのスコープはpublicである)
val publicMemberVal = 1
protected val protectedMemberVal1 = 2
private val privateMemberVal1 = 3
//メンバ関数(デフォルトでのスコープはpublicである)
def publicFunc = {}
private def privateFunc = {}
}
