26

Scala が ALGOL からの名前による呼び出しをサポートしていることは知っています。それが何を意味するかは理解できると思いますが、Scala は C#、VB.NET、および C++ のように参照による呼び出しを行うことができますか? Java が call-by-reference を実行できないことは知っていますが、この制限が言語だけによるものなのか、それとも JVM によるものなのかはわかりません。

これは、巨大なデータ構造をメソッドに渡したいが、そのコピーを作成したくない場合に便利です。この場合、参照渡しは完璧に思えます。

4

4 に答える 4

51

Java と Scala はどちらも、値がプリミティブまたはオブジェクトへのポインターであることを除いて、値渡しのみを使用します。オブジェクトに変更可能なフィールドが含まれている場合、これと参照による呼び出しの間に実質的な違いはほとんどありません。

オブジェクト自体ではなくオブジェクトへのポインターを常に渡すため、巨大なオブジェクトを繰り返しコピーする必要はありません。

ちなみに、Scala の名前による呼び出しは、値による呼び出しを使用して実装されており、値は式の結果を返す (ポインターへの) 関数オブジェクトです。

于 2011-01-25T04:47:47.620 に答える
4

「すべてがオブジェクト」であり、オブジェクト参照にアクセスできない言語 (Java や Scala など) の場合、すべての関数パラメーターは、言語の下の抽象化レベルで値渡しされる参照です。ただし、言語抽象化のセマンティクスの観点からは、関数に参照先オブジェクトのコピーが提供されるかどうかに応じて、参照渡しまたは値渡しのいずれかが存在します。この場合、call-by-sharing という用語は、抽象化の言語レベルで call-by-reference と call-by-value の両方を含みます。したがって、Java は、言語セマンティクスより下の抽象レベルで値渡しであると言うのは正しいです (つまり、仮想マシンの C またはバイトコードに仮想的に変換される方法と比較して)。

Java と Scala では、特定の組み込み (a/k/a プリミティブ) 型は自動的に値渡し (int や Int など) になり、すべてのユーザー定義型は参照渡しになります (つまり、手動でコピーしてのみ渡す必要があります)。それらの値)。

これをより明確にするために、 Wikipedia のCall-by-sharing セクションを更新したことに注意してください。

おそらくウィキペディアは、値渡しと値渡しの違いについて混乱しているでしょうか? 値渡しは、代入式や関数適用に適用されるため、より一般的な用語だと思いました。私はウィキペディアでその修正をしようとはしませんでした。

オブジェクトが不変である場合、「すべてがオブジェクトである」セマンティクスのレベルでは、参照による呼び出しと値による呼び出しの間に違いはありません。したがって、値による呼び出しと参照による呼び出しの宣言を可能にする言語 (私が開発している Scala のような言語など) は、オブジェクトが変更されるまで値によるコピーを遅らせることで最適化できます。


これに反対票を投じた人々は、「call-by-sharing」が何であるかを理解していないようです。

以下に、私の Copute 言語 (JVM をターゲットとする) について書いたことを追加し、評価戦略について説明します。


純粋であっても、評価戦略を選択する必要があるため、チューリング完全言語 (つまり、再帰を許可する言語) は完全に宣言的ではありません。評価戦略は、関数とその引数の間の相対的なランタイム評価順序です。すべての式は関数であるため、関数の評価戦略は厳密または非厳密にすることができます。これは、それぞれ熱心または遅延と同じです。Eager は、関数が評価される前に引数式が評価されることを意味します。一方、lazy は、引数式が関数で最初に使用される実行時にのみ (1 回) 評価されることを意味します。評価戦略は、パフォーマンス、決定論、デバッグ、および操作上のセマンティクスのトレードオフを決定します。純粋なプログラムの場合、表示のセマンティクスの結果は変更されません。

基本的に、すべての式は関数 (の合成) です。つまり、定数は入力のない純粋な関数であり、単項演算子は 1 つの入力を持つ純粋な関数です。2 項演算子は 2 つの入力を持つ純粋な関数です。 while) は関数でモデル化できます。これらの関数を評価する順序は、構文によって定義されません。たとえば、f( g() ) は g を熱心に評価し、次に g の結果に対して f を評価するか、f を評価し、その結果が f 内で必要な場合にのみ g を遅延評価することができます。

前者 (eager) は call-by-value (CBV) で、後者 (lazy) は call-by-name (CBN) です。CBV には、Java、Python、Ruby などの最新の OOP 言語で普及しているバリアント call-by-sharing があり、不純な関数がいくつかの変更可能なオブジェクトを参照によって暗黙的に入力します。CBN にはバリアント call-by-need (CBN) があり、関数の引数は 1 回だけ評価されます (メモ化関数とは異なります)。Call-by-need は指数関数的に高速であるため、ほとんどの場合、call-by-name の代わりに使用されます。通常、CBN の両方のバリアントは、宣言された関数階層と実行時の評価順序との間に不一致があるため、純粋にのみ表示されます。

言語には通常、デフォルトの評価戦略があり、オプションで関数を非デフォルトで評価する構文を持っているものもあります。デフォルトで熱心な言語は、通常、論理積 (a/k/a "and"、&&) および論理和 (a/k/a "or"、||) 演算子を遅延して評価します。これは、2 番目のオペランドが必要ないためです。半分の場合、つまり true || 何でも == 真と偽 && 何でも == 偽。

于 2011-02-16T03:48:44.423 に答える
0

Scala で参照パラメーターをエミュレートする方法は次のとおりです。

def someFunc( var_par_x : Function[Int,Unit] ) {
    var_par_x( 42 ) // set the reference parameter to 42
}

var y = 0 
someFunc( (x => y=x) )
println(y)

そうですね、正確には Pascal や C++ プログラマーが慣れているものとは異なります。しかし、Scala にはほとんどありません。利点は、これにより、パラメーターに送信された値で何ができるかについて、呼び出し元がより柔軟になることです。例えば

someFunc( (x => println(x) ) )
于 2015-03-28T20:56:52.087 に答える