web-dev-qa-db-ja.com

非常に多くの言語が値で渡されるのはなぜですか?

Cのような明示的なポインター操作がある言語でさえ、常に値で渡されます(あなたはcan参照で渡せますが、それはデフォルトの動作ではありません)。

これの利点は何ですか、なぜ非常に多くの言語が値によって渡されるのですか他の言語はなぜ参照によって渡されるのですか? (私はよくわかりませんが、Haskellが参照によって渡されることを理解しています)。

37

メソッド/関数のパラメーターを誤って変更することがないため、値渡しは参照渡しよりも安全です。これにより、関数に与える変数を気にする必要がないため、言語の使用が簡単になります。あなたはそれらが変更されないことを知っています、そしてこれはあなたがしばしばexpectです。

ただし、パラメーターを変更するためにwantを使用する場合は、これを明確にするための明示的な操作(ポインターを渡す)が必要です。これにより、すべての発信者がわずかに異なる方法で電話をかけることになります(&variable、C)で、変数パラメーターを変更できることを明示します。

したがって、明示的に(ポインターを渡すことを要求することによって)変更されていない限り、関数は変数パラメーターを変更しないと想定できます。これは、他の方法よりも安全でクリーンなソリューションです。特に、変更できないと述べていない限り、すべてがパラメーターを変更できると仮定します。

59
Oleksi

Call-by-valueとcall-by-referenceは、ずっと前にパラメーター受け渡しモードと間違われていた実装手法です。

初めに、FORTRANがありました。サブルーチンはパラメーターを変更できなければならず、計算サイクルが多すぎて複数のパラメーター受け渡しモードを許可できなかったため、FORTRANには参照による呼び出ししかありませんでした。さらに、FORTRANが最初に定義されたときのプログラミングについては十分に知られていませんでした。

ALGOLは、名前による呼び出しと値による呼び出しを考え出しました。値による呼び出しは、変更することが想定されていないもの(入力パラメーター)に対するものでした。名前による呼び出しは、出力パラメーター用でした。名前による呼び出しは主要な廃人であることが判明し、ALGOL 68はそれを破棄しました。

Pascalは、値による呼び出しと参照による呼び出しを提供しました。プログラマがコンパイラにラージオブジェクト(通常は配列)を参照で渡すことを伝え、パラメータスタックのブローを回避する方法は提供しませんでしたが、オブジェクトは変更しないでください。

Pascalは、言語設計レキシコンへのポインタを追加しました。

Cは、メモリ内の任意のオブジェクトへのポインターを返すクラッジ演算子を定義することにより、値による呼び出しとシミュレートされた参照による呼び出しを提供しました。

後の言語はCをコピーしましたが、これは主にデザイナーが他に何も見たことがないためです。これが、値による呼び出しが非常に人気がある理由です。

C++は、参照による呼び出しを提供するために、Cクラッジの上にクラッジを追加しました。

現在、値による呼び出し、参照による呼び出し、ポインターによる呼び出しの直接的な結果として、CおよびC++(プログラマー)は、constポインターとconstへのポインター(読み取り専用)でひどい頭痛を抱えています。オブジェクト。

エイダはこの悪夢全体をなんとか避けました。

Adaには、値による呼び出しと参照による呼び出しの明示的な呼び出しはありません。むしろ、Adaにはinパラメータ(読み取りはできるが書き込まれない)、outパラメータ(読み取り前に書き込む必要がある)、およびinパラメータがあり、任意の順序で読み取りと書き込みを行うことができます。コンパイラーは、特定のパラメーターが値によって渡されるのか、参照によって渡されるのかを決定します。これは、プログラマーには透過的です。

55
John R. Strohm

参照渡しは、意図しない動作を引き起こし始めたときに追跡することが非常に困難であり、追跡することが非常に困難な非常に微妙な意図しない副作用を可能にします。

値渡し、特にfinalstaticまたはconst入力パラメーターを使用すると、このクラスのバグ全体が消えます。

不変言語はさらに確定的であり、関数の内容や関数から得られることが予想されるものについての推論や理解が容易です。

13
user7519

非常に多くの言語が値で渡されるのはなぜですか?

大きなプログラムを小さなサブルーチンに分割するポイントは、サブルーチンについて独立して推論できることです。参照渡しはこのプロパティを壊します。 (共有の可変状態も同様です。)

Cのような明示的なポインター操作がある言語でも、常に値で渡されます(あなたはcan参照で渡せますが、これはデフォルトの動作ではありません)。

実際には、Cはalways値渡しであり、参照渡しではありません。何かのアドレスを取得してそのアドレスを渡すことができますが、アドレスは引き続き値で渡されます。

これの利点は何ですか、なぜ非常に多くの言語が値によって渡されるのですかなぜ他の言語は参照によって渡されるのですか

参照渡しを使用する主な理由は2つあります。

  1. 複数の戻り値のシミュレーション
  2. 効率

個人的には、#1は偽物だと思います。それは、ほとんどの場合、APIや言語設計が悪いことの言い訳です。

  1. 複数の戻り値が必要な場合は、それらをシミュレートせず、それらをサポートする言語を使用してください。
  2. タプルなどの軽量データ構造にパッケージ化することで、複数の戻り値をシミュレートすることもできます。これは、言語がパターンマッチングまたはバインド解除をサポートしている場合に特に効果的です。例えば。ルビー:

    def foo
      # This is actually just a single return value, an array: [1, 2, 3]
      return 1, 2, 3
    end
    
    # Ruby supports destructuring bind for arrays: a, b, c = [1, 2, 3]
    one, two, three = foo
    
  3. 多くの場合、複数の戻り値は必要ありません。たとえば、一般的なパターンの1つは、サブルーチンがエラーコードを返し、actualの結果が参照によって書き戻されることです。代わりに、エラーが予期しない場合は例外をスローするか、エラーが予想される場合はEither<Exception, T>を返します。別のパターンは、操作が成功したかどうかを示すブール値を返し、参照によって実際の結果を返すことです。繰り返しになりますが、失敗が予期しないものである場合、失敗が予想される場合は、代わりに例外をスローする必要があります。辞書で値を検索する場合は、代わりにMaybe<T>を返す必要があります。

値をコピーする必要がないため、参照渡しは値渡しよりも効率的です。

(私はよくわかりませんが、Haskellが参照渡しされることを理解しています)。

いいえ、Haskellは参照渡しではありません。また、値渡しでもありません。参照渡しと値渡しはどちらも厳密な評価戦略ですが、Haskellは厳密ではありません。

実際、Haskell仕様では、特定の評価戦略anyは指定されていません。ほとんどのHakell実装では、名前による呼び出しと必要による呼び出しの組み合わせ(メモによる名前による呼び出しの変形)を使用していますが、標準ではこれを義務付けていません。

厳密な言語であっても、関数型言語の参照渡しと値渡しを区別しても意味がないことに注意してください。これらの違いは、参照を変更した場合にのみ確認できるためです。したがって、実装者は、言語のセマンティクスを壊すことなく、2つを自由に選択できます。

7
Jörg W Mittag

言語の呼び出しモデル、引数のタイプ、および言語のメモリモデルに応じて、異なる動作があります。

単純なネイティブ型では、値で渡すことにより、レジスターによって値を渡すことができます。値をメモリからロードする必要もセーブバックする必要もないため、これは非常に高速です。オブジェクトの呼び出し側のコピーを混乱させる危険を冒すことなく、呼び出し先が引数を使い終わったら、引数によって使用されるメモリを再利用するだけで、同様の最適化も可能です。引数が一時オブジェクトである場合、おそらくそれを行うことでフルコピーを保存することになります(C++ 11は、新しい右参照とその移動セマンティクスにより、この種の最適化をさらに明確にします)。

多くのOO言語(C++はこのコンテキストでは例外です)では、オブジェクトを値で渡すことはできません。強制的に参照で渡す必要があります。これにより、コードはデフォルトでポリモーフィックになりますおよびOOに固有のインスタンスの概念とより一致します。また、値で渡す場合は、そのようなアクションを生成したパフォーマンスのコストを認識して、自分でコピーを作成する必要があります。この場合、言語は最高のパフォーマンスが得られる可能性が高いアプローチ。

関数型言語の場合、値または参照による受け渡しは、単に最適化の問題だと思います。そのような言語の関数は純粋で副作用がないため、速度を除いて値をコピーする理由はありません。そのような言語が同じ値のオブジェクトの同じコピーを共有することがよくあると私はかなり確信しています。(const)参照渡しのセマンティクスを使用した場合にのみ利用できる可能性があります。 Pythonは整数と一般的な文字列(メソッドやクラス名など)にもこのトリックを使用し、整数と文字列がPythonで定数オブジェクトである理由を説明します。これにより、コードを再び最適化するのに役立ちます。コンテンツ比較ではなくポインタ比較の例、および一部の内部データの遅延評価を行っています。

3
Fabien Ninoles

参照渡しする場合は、グローバルのすべての問題(つまり、スコープと意図しない副作用)を伴うグローバル値を効果的に使用していることになります。

参照は、グローバルと同様に有益な場合がありますが、最初に選択するべきではありません。

2
jmoreno

値によって式を渡すことができますが、これは自然なことです。 (一時的な)参照による式の受け渡しは...奇妙です。

2
herby