Scalaでは、変数を宣言する前に「lazy」キーワードを付与することで、最初に参照されるまで変数を初期化しないようにすることができます。
この変数は最初の参照時に1度計算されたら、その後計算されることはありません。そのため、アプリケーションの起動を高速化できます。
遅延評価変数を使ってみましょう。通常の変数xと遅延評価変数lazyXを定義します。lazyXではメッセージ表示後、xに+1した数を返します。
scala> val x = 1 x: Int = 1 scala> lazy val lazyX = { println("init val"); x + 1 } lazyX: Int = <lazy>
lazyXを参照してみます。
scala> lazyX init val res3: Int = 2 scala> lazyX res4: Int = 2
最初の参照ではメッセージが表示され、値が計算されています。2回目の参照では最初に計算した値を返しているだけです。
通常、関数パラメータは値渡しですが、名前渡しパラメータを使えば、引数が必要になったタイミングで評価させることが可能です。
名前渡しパラメータは下記のように、関数/パラメータに通常使う丸括弧を省略することで指定します。
引数名: => 引数の型
REPLで引数の遅延評価を試してみましょう。while文のような動作を実現する、myWhile関数を作成してみます。
def myWhile(conditional: => Boolean)(f: => Unit) { println("myWhile") if (conditional) { f myWhile(conditional)(f) } }
条件文を指定するconditional部分は名前渡しパラメータになっています。第2引数には、条件が真の場合に実行する処理を記述します。myWhileを使ってみましょう。
scala> var count = 0 count: Int = 0 scala> myWhile(count < 3) { | println("count=" + count) | count += 1 | } myWhile count=0 myWhile count=1 myWhile count=2
条件が真の間、第2引数に指定した処理がmyWhile関数の中で実行されています。
通常、関数に引数を渡す場合は引数に指定された型か、そのサブクラスである必要があります。しかし、この「構造的部分型(Structural Subtyping)」を使うと、“継承関係がなくても特定のシグネチャを持つメソッドを持っていれば、引数に指定した型の派生型として渡す”ことが可能になります。
以前の連載第9回の「Scalaの抽象型と暗黙の型変換/引数、パラメータ制約」では、「typeキーワード」を使って型に別名を付ける方法を紹介しました。
typeキーワードは別名を付けるだけではなく、型名を指定せずに「特定のシグネチャの関数を持つ」型を定義できます。そして、コンパイル時に「特定のシグネチャの関数を持つ」型が渡されているかどうかチェックしてくれます。
実際にREPLで例を示してみましょう。まずは、typeキーワードを使ってIO型を定義します。
type IO = { def open(src:String):Boolean def close:Unit }
IO型は「String型の引数を1つとり、Boolean型を返す『open』という名前の関数」と「引数、戻り値なしの『close』という名前の関数」を持つ構造的部分型として定義されています。この2つの関数を持っていれば、IO型の派生型として使えます。
次に、このIO型を受け取る関数を定義します。
def func(io:IO,src:String) = { io.open(src) println("func execute") io.close }
func関数に渡すクラスを定義しましょう。今回はFileIOクラスとDatabaseIOクラスを用意します。IO型の派生型と見なされるために。openメソッドとcloseメソッドを定義しましょう。
class FileIO { def open(src:String):Boolean = { println(src + " file open"); true } def close:Unit = println("file close") } class DatabaseIO { def open(url:String):Boolean = { println(url + " connection open"); true } def close = println("connection close") }
func関数にFileIOとDatabaseIOを渡してみます。2つのクラスに継承関係はありませんが、どちらも問題なく渡せます。
scala> func(new FileIO,"test.txt") test.txt file open func execute file close scala> func(new DatabaseIO,"databaseUrl") databaseUrl connection open dunc execute connection close
RubyやPythonでは「ダックタイピング」という手法が使えますが、構造的部分型を使えば、静的にダックタイピングと似たような動作をさせることができます。
Copyright © ITmedia, Inc. All Rights Reserved.