2013-09-26 12 views
8

私はcall-by-nameとcall-by-valueという基本的な概念を理解しており、いくつかの例を見てきました。しかし、私はcall-by-nameをいつ使うべきかについてはっきりしていません。コール・バイ・ネームが他のコール・タイプに比べて大きな利点やパフォーマンスを得る現実的なシナリオは何でしょうか?方法を設計する際にコールタイプを選択するには、正しい考え方のアプローチが必要ですか?それが説明される可能性がありますコール・バイ・ネームとコール・バイ・バリューを使用するタイミングは?

+0

例がありますか? – dax

答えて

16

名前による呼び出しが多かったため、パフォーマンスや正確さが増す可能性があります。

単純なパフォーマンス例:ロギング。

trait Logger { 
    def info(msg: => String) 
    def warn(msg: => String) 
    def error(msg: => String) 
} 

そして、このように使用:このようなインタフェースを想像してみて

logger.info("Time spent on X: " + computeTimeSpent) 

(たとえば、ログレベルがより高いために構成された、ので)infoメソッドは何もしていない場合、computeTimeSpentが呼び出されることはなく、時間を節約できます。これはロギングでは頻繁に起こります。ロギングでは、ロギングされるタスクに比べて高価な文字列操作が頻繁に発生します。

正解例:論理演算子。

おそらく、このようなコードを見てきました:refnullあるたびisSomethingがするので、あなたがエラーを取得します、そして

trait Boolean { 
    def &&(other: Boolean): Boolean 
} 

if (ref != null && ref.isSomething) 

は、あなたがこのような&&メソッドを宣言したと言いますnull参照で呼び出された後、&&に渡されます。このため、実際の宣言は次のようになります。

実際には、値による呼び出しを使用するのは不思議です。実際、Haskellプログラミング言語では、すべてがcall-by-nameがどのように動作するか(似ていますが同じではありません)と同様に動作します。

call-by-nameを使用しない理由としては、速度が遅く、クラスが多く作成され(読み込みに時間がかかります)、メモリが多く消費されます。それについて。

+0

上記の説明にはわずかな修正があります。この例では、ログレベルが情報レベルより高く設定されていても、インフォメーションロガー内で呼び出された関数が呼び出されます。ロガーメッセージのみが印刷されません。 – Sangeeta

+0

@Sangeetaいいえ、関数は呼び出されません。それは全体のポイントです。 –

4

簡単な方法は、

コールバイ値関数は、渡された式の値 関数を呼び出す前に、これと同じ値がアクセスされたすべての 時間を計算です。ただし、call-by-name関数は、渡された 式の値がアクセスされるたびにその値を再計算します。

私はいつもこの用語が不必要に混乱していると思っています。関数は、名前によるコールと値によるコールの状態が異なる複数のパラメータを持つことができます。したがって、関数が名前によるコールまたは値による呼び出しではなく、それぞれのパラメータが名前渡しまたは値渡しである可能性があります。さらに、「名前による呼び出し」は名前とは関係ありません。 => IntはIntとは異なる型です。それは "Intを生成する引数のない関数"です。ファーストクラスの関数を取得したら、これを記述するために名前による呼び出しの用語を使用する必要はありません。

6

名前で呼び出すと値にアクセスするときに評価され、値で呼び出すと値が最初に評価されてからメソッドに渡されることを意味します。

この違いを見るには、この例(副作用のみの完全な非機能プログラミング)を参照してください)。いくつかの操作にかかる時間を測定する関数を作成したいとします。あなたがそれを行うことができますによる呼び出し名:

Starting to measure time 
Will now sleep a little 
Operation took 1000167919 ns 

しかし、あなたはmeasuremeasure(action: Unit)への署名だけを変更した場合ので、それはpass-使用しています:あなたは結果(メーリングリスト)を取得します

def measure(action: => Unit) = { 
    println("Starting to measure time") 
    val startTime = System.nanoTime 
    action 
    val endTime = System.nanoTime 
    println("Operation took "+(endTime-startTime)+" ns") 
} 

measure { 
    println("Will now sleep a little") 
    Thread.sleep(1000) 
} 

値による、結果は次のようになります。

Will now sleep a little 
Starting to measure time 
Operation took 1760 ns 

あなたが見ることができるように、actionは、経過時間が近いさえも開始しmeasure前に評価されますメソッドが呼び出される前に既に実行されていたアクションのため、0に設定します。

ここで、値渡しにより、メソッドの予想される動作を実現できます。場合によっては、正確性に影響を与えませんが、たとえば結果が使用されない場合に複雑な式を評価する必要がないロギングフレームワークなど、パフォーマンスに影響を与えます。

0

名前でcall-by-nameパラメータを複数回使用すると、パラメータは複数回評価されます。

渡されるパラメータは、関数型プログラミングによる純粋な関数呼び出しである必要があるため、呼び出される関数内の各評価は常に同じ結果を生成します。したがって、名前によるコールは、従来のコールバイ値よりも無駄になります。

関連する問題