3

さて、Ruby は「値渡し」です。しかし、Ruby で「参照渡し」と「値渡し」を正確に定義するにはどうすればよいでしょうか。この回答を使用しました参照渡しと値渡しの違いは何ですか? それによると、Ruby はハイブリッドのようです...

技術的には、Rubyは「値渡し」のようですが、メソッドに渡すときに値がコピーされないという違いがあります。「値」= オブジェクト、「参照」= そのオブジェクトを指す参照変数を定義すると、「特定のオブジェクトを指す参照変数を渡す」と同等の場合、「参照渡し」は理にかなっていますか? そして、「参照」が渡されると、メソッドはオブジェクトのコピーを作成しませんが、実際には、直接操作できる元のオブジェクト自体 (変数によって参照される) を持っています。私が間違っている場合は修正してください。

編集:私はこの質問を認識していますRubyは参照渡しですか、それとも値渡しですか? しかし、人によって参照/値渡しの定義が異なるようです。

4

3 に答える 3

5

Ruby は、C、Java、Python、Smalltalk、ECMAScript などと同様に値渡しです。C++ と C# もデフォルトで値渡しです。参照渡しを使用するには、特別な注釈 ( &C++ の場合ref、C# の場合) を使用する必要があります。

違いは実際にはかなり単純です。参照が渡された場合、呼び出し先はそれを変更できますが、そうでない場合は変更できません。Ruby では、呼び出し先は参照を変更できないため、値渡しになります。

def is_ruby_pass_by_value?(foo)
  foo = 'No, Ruby is pass-by-reference.'
  return nil
end

bar = 'Yes, of course, Ruby *is* pass-by-value!'

is_ruby_pass_by_value?(bar)

p bar
# 'Yes, of course, Ruby *is* pass-by-value!'

ご覧のとおり、 method 内is_ruby_pass_by_value?では参照bar/fooが渡されていません。そうしないと、後で変更が表示されます。barつまり、参照そのものではなく、 (それに含まれる値) の内容が渡されます。bar

では、渡されている値は何でしょうかそれはオブジェクトではありません。Stringむしろ、そのオブジェクトへのポインターです。Stringより正確には、そのポインターのコピーです。

現在、そのオブジェクトへの 2 つのポインターがあります。Stringそして、そのStringオブジェクトは変更可能です! したがって、一方のポインタ ( foo) をたどってそのStringオブジェクト自体を変更するように指示し、次にもう一方のポインタ ( bar) をたどってその内容について尋ねると、明らかに変更された内容が表示されます。これは共有可変状態の性質にすぎません。Ruby は、純粋に機能的で参照透過的な言語ではありません。

def is_ruby_pass_by_value?(foo)
  foo.replace('More precisely, it is call-by-object-sharing!')
  foo = 'No, Ruby is pass-by-reference.'
  return nil
end

bar = 'Yes, of course, Ruby *is* pass-by-value!'

is_ruby_pass_by_value?(bar)

p bar
# 'More precisely, it is call-by-object-sharing!'

実際、Ruby では、変数に保持され、引数として渡される値は常にポインターです。これが、ほぼすべてのオブジェクト指向言語の仕組みです。Barbara Liskov は、値渡しのこの特別なケースを「call-by-object-sharing」と呼び、「call-by-sharing」または「call-by-object」と呼ばれることもあります。

ただし、渡される値がポインターであるという事実は、まったく無関係であることに注意してください。値渡しと参照渡しは、引数が何であるかではなく、引数がどのように渡されるかについてです。C は、渡すかポインターを渡すかに関係なく、常に値渡しです。ポインターはまだ値渡しされています。Rubyでも同様に、ポインタは値渡しされています。Ruby と C の違いは、a) Ruby ではポインターのみを渡すことができること、および b) ポインターを渡すことを示す特別な構文がないことです。int

[注: ほとんどの Ruby 実装では、実際には、ポインターよりも小さいオブジェクトをそのオブジェクトへのポインターを渡す代わりに直接渡すための最適化が行われています。ただし、言語仕様によって完全に不変であることが保証されているオブジェクトに対してのみそれを行うため、値へのポインターを渡すことと値を直接渡すことの違いを観察することは不可能です。これは、たとえば、Fixnums、Symbols、Floats、nilおよびtrueに対して行われfalseます。]

以下は C# の例で、値渡し (その値が参照であっても) と参照渡しの違いを示しています。

class Program
{
    static void IsCSharpPassByValue(string[] foo, ref string baz)
    {
        foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
        foo = new string[] { "C# is not pass-by-reference." };

        baz = "It also supports pass-by-reference if explicitly requested.";
    }

    static void Main(string[] args)
    {
        var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };

        var grault = "This string will vanish because of pass-by-reference.";

        IsCSharpPassByValue(quux, ref grault);

        Console.WriteLine(quux[0]);
        // More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.

        Console.WriteLine(grault);
        // It also supports pass-by-reference if explicitly requested.
    }
}
于 2013-11-10T12:51:08.307 に答える
2

これについて純粋主義者に言って、ルビーは「参照の値を渡す」という特殊なケースであると言うことができますが、それは要点を逃しています。Ruby のすべてがオブジェクトであると考えてみてください

foo(14) は、値が 14 の整数オブジェクトへの参照を渡します。裏で処理が行われているため、最終的に 14 個のオブジェクトが 100 個になることはありませんが、目的の観点からすると、ほとんどの概念を忘れることができる時間。

于 2013-11-10T12:00:38.603 に答える
2

Rubyは「参照渡し」です。違いは次のとおりです。参照渡しの場合、元のオブジェクトに悪いことができます。

x = [ "virgin" ]

def do_bad_things_to( arg )
  arg.clear << "bad things"
end

do_bad_things_to( x )

値渡しの場合、元のオブジェクトの値を取得して操作できますが、元のオブジェクトに悪いことをすることはできません。元のオブジェクトの値のコピーもメモリを消費するため、より多くのメモリを消費します。

def pass_by_value( arg )
  arg.dup
end

y = [ "virgin" ]

do_bad_things_to( pass_by_value( y ) )

p x #=> [ "bad things" ]
p y #=> [ "virgin" ]

不変オブジェクト (数字、記号、truefalsenil...) に対して、その不変性のおかげで悪いことをすることはできません。Ruby では、これらは値渡しであるとよく言われますが、実際には、メモリ内に内部のコピーを多数保持することがほとんど意味をなさないのと同じように、それらの区別はほとんど意味がありません。

更新: 「参照」が何を意味するかについて、用語の競合があるようです。Ruby では、Jörg Mittag の「参照渡し」は、ローカル変数を閉じるクロージャーによって明示的に実現されます。

baz = "Jörg"
define_method :pass_by_Jorgs_reference_to_baz do baz = "Boris" end
pass_by_Jorgs_reference_to_baz
baz #=> "Boris"
于 2013-11-10T12:56:27.253 に答える