4.2 パラメータ

def を使って、パラメータを持った関数を定義できます。たとえば

scala> def square(x: Double) = x * x square: (Double)Double scala> square(2) unnamed0: Double = 4.0 scala> square(5 + 3) unnamed1: Double = 64.0 scala> square(square(4)) unnamed2: Double = 256.0 scala> def sumOfSquares(x: Double, y: Double) = square(x) + square(y) sumOfSquares: (Double,Double)Double scala> sumOfSquares(3, 2 + 2) unnamed3: Double = 25.0

関数のパラメータは関数名の後に置かれ、常に括弧で囲まれます。各パラメータは型を伴い、型はパラメータ名とコロンに続いて示されます。現時点では、倍精度数の scala.Double 型のような基本的な数値型だけを必要とします。Scala では、いくつかの標準的な型について 型エイリアス が定義されていて、数値型は Java と同じように書けます。たとえば double は scala.Double の型エイリアスであり、int は scala.Int の型エイリアスです。

パラメータをもつ関数は、式の演算子と同じように評価されます。はじめに関数の引数が (左から右の順序で) 評価されます。つぎに関数適用が関数の右辺で置き換えられ、同時に関数のすべての形式上のパラメータが対応する実際の引数で置き換えられます。

Example 4.2.1

sumOfSquares(3, 2+2) → sumOfSquares(3, 4) → square(3) + square(4) → 3 * 3 + square(4) → 9 + square(4) → 9 + 4 * 4 → 9 + 16 → 25

この例は、インタプリタが関数適用の前に、関数の引数を値に簡約することを示しています。この代わりに、簡約されていない引数に関数適用することも選択できます。それは次の簡約列をもたらします。

sumOfSquares(3, 2+2) → square(3) + square(2+2) → 3 * 3 + square(2+2) → 9 + square(2+2) → 9 + (2+2) * (2+2) → 9 + 4 * (2+2) → 9 + 4 * 4 → 9 + 16 → 25

二つ目の評価順序は 名前渡し (call-by-name)として、はじめの例は 値渡し (call-by-value)として知られています。純粋な関数だけを使用する式や、したがって、置き換えモデルで簡約可能な式では、どちらの枠組みでも同じ値となります。

値渡しには、引数評価を繰り返さないという利点があります。名前渡しには、パラメータが関数で全く使用されない時に引数の評価を避けるという利点があります。ふつう、値渡しは名前渡しより効率的ですが、値渡し評価は、名前渡し評価なら終了する箇所でループするかもしれません。次を考えてみましょう。

scala> def loop: Int = loop loop: Int scala> def first(x: Int, y: Int) = x first: (Int,Int)Int

すると、first(1, loop) は名前渡しでは 1 に簡約されますが、値渡しでは繰り返し自分自身へと簡約され、したがって評価は終了しません。

first(1, loop) → first(1, loop) → first(1, loop) → ...

Scala はデフォルトでは値渡しを使いますが、パラメータ型の前に => が置かれた場合は名前渡し評価へと切り替えます。

Example 4.2.2

scala> def constOne(x: Int, y: => Int) = 1 constOne: (Int,=> Int)Int scala> constOne(1, loop) unnamed0: Int = 1 scala> constOne(loop, 2) // 無限ループとなる ^C // Ctrl-C で実行を停止