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 実装では、実際には、ポインターよりも小さいオブジェクトをそのオブジェクトへのポインターを渡す代わりに直接渡すための最適化が行われています。ただし、言語仕様によって完全に不変であることが保証されているオブジェクトに対してのみそれを行うため、値へのポインターを渡すことと値を直接渡すことの違いを観察することは不可能です。これは、たとえば、Fixnum
s、Symbol
s、Float
s、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.
}
}