Scalaでの値による呼び出しと名前による呼び出し
この記事では、値による呼び出しと名前による呼び出しの機能と、Scala でのそれらの評価戦略について学びます。 そして、それらを実際に使用する方法について説明します。
Scala の値による呼び出し
Scala の関数引数は、デフォルトで値渡しと見なされます。 小さな例を見てみましょう:
def example(x:Int) = x*x
上記のコードでは、値による呼び出しとして引数を取る関数 example
を定義しました。
一般に、Scala の演算子と同様に、パラメーター化された関数はコンパイラーによって同じ方法で評価されます。 まず、すべての関数の引数が左から右に評価されます。 次に、すべての関数適用が関数の右辺に置き換えられます。
同時に、関数のすべての仮パラメータが実際のパラメータに置き換えられます。
値渡し戦略の大きな利点の 1つは、すべての関数引数が 1 回だけ評価されることです。
Scala での名前による呼び出し
関数の引数の先頭に =>
(記号以上) を追加するだけで、引数を名前で呼び出すことができます。
理解を深めるために、小さな例を見てみましょう。
def example(x: =>Int) = x*x
上記のコードのパラメーター x
は、値によって呼び出されます。 名前による呼び出しの評価は値による呼び出し戦略と同じですが、1つの大きな利点があります。それは、対応する値が関数本体内で使用されるまで、関数が評価されないことです。
Scala での値による呼び出しと名前による呼び出しの戦略
2つのパラメータを持つ関数 add
があるとします: x
は int 型で値によって呼び出され、y
は int 型で名前で呼び出されます。
def add(x: Int, y:=>Int) = x+x
値渡し戦略を使用して add
関数がどのように評価されるかを見てみましょう。
assert(add(3+3,8) == 12)
ここで、値による呼び出しは最初に式 3+3
を評価し、次に値 6
を関数の本体に渡します。 パラメータ x
がそれ自体に追加され、最終的な答えが 12
として計算されます。
次に、名前による呼び出し戦略を使用して評価された同じ関数を見てみましょう。
assert(add(8,3+3) == 16)
この場合、パラメーター y
は関数の本体内で使用されないため、3+3
は名前による呼び出し戦略によって無視されます。 したがって、x
がそれ自体に追加され、最終的な答えが 16
として計算されます。
この場合、名前による呼び出し戦略は、値による呼び出し戦略よりも少し高速であると言えます。
もう 1つの例を見てみましょう。再帰的に自分自身を無限に呼び出す関数があります。
def myFun(): Int = myFun() + 1
ここで、この関数を add
関数のパラメーター x
として使用すると、call by value
引数 x
は StackOverflowError
を返します。
assertThrows[StackOverflowError]
{
add(myFun(),5)
}
上記のコードでは、myFun()
メソッドが関数本体の前に評価されるため、関数を呼び出すと StackOverflowError
が返されます。 対照的に、myFun
メソッドを名前による呼び出し引数として使用すると、関数呼び出しは関数本体内で評価されないため、例外をスローすることなく正常に実行されます。
assert(add(5, myFun()) == 10)
私たちのコードで使用するのに適したもの
上記の例に基づいて、名前による呼び出し戦略は、渡された引数が関数本体内で評価されない可能性があるため、より効率的であるため、より魅力的です。
しかし、全体的に見ると、値による呼び出し戦略は、引数の繰り返し計算が回避されるため、名前による呼び出しよりも効率的です。 さらに、引数がコードでいつ評価されるかがわかっているため、副作用を回避できます。
まとめ
この記事では、Scala の名前による呼び出しと値による呼び出しの戦略について学びました。 それらがどのように評価され、その使用法が見られるかを学び、ほとんどの場合、値による呼び出し戦略を使用する方が良いと結論付けました。