一般的に、引数は二つの方法がある。一つが値渡しで、もう一つが参照渡しである。
値渡しの概念はとても簡単。
- 受け取るデータを格納する変数 (仮引数) を用意する。
- データを引数に代入する。
- 関数の実行終了後、引数を廃棄する。
値渡しのポイントは2番目。データを引数に代入するとき、データのコピーが行われる。
1
2
3
4
5
6
7
8
9
10
11
| irb> def foo(a)
irb> a = 100
irb> print a
irb> end
=> :foo
irb> x = 10
=> 10
irb> foo x
100=> nil
irb> x
=> 10
|
foo
が参照渡しであれば、仮引数a
の値を100に書き換えると、実引数であるx
の値も100になるはず。foo(x)
を呼び出したあと、x
の値は10のままなので、Rubyは「値渡し」であることがわかる。
1
2
3
4
5
6
7
8
9
10
11
12
13
| irb> def print_obj_id arg
irb> p "before change : #{int.object_id}"
irb> arg = 10
irb> p "after changed : #{int.object_id}"
irb> arg
irb> end
irb> int = 1
irb> p "int before func : #{int}"
irb> p "obj_id outside func : #{int.object_id}"
irb> p "excute func : #{print_obj_id int}"
irb> p "int after excuted func : #{int.object_id}"
|
このコードに対する結果は下記のようになる。
1
2
3
4
5
6
| "int before func : 1"
"obj_id outside func : 3"
"before change : 3"
"after changed : 21"
"excute func : 10"
"int after excuted func : 3"
|
これは渡された仮引数のarg
が変わっても実引数の変数int
は変わらないことを証明する。
しかし、配列、hashなどはmutable(書き換え可)
なオブジェクトなので、関数の引数に配列やhashを渡してそれを修正すると、呼び出し元の変数の値も書き換えられるようになる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| # arrayの場合
irb> def bar(x, y)
irb> x.push y
irb> x[0] = 10
irb> end
=> :bar
irb> a = [1, 2, 3]
=> [1, 2, 3]
irb> a.object_id
=> 70284299180520
irb> bar a, 4
=> 10
irb> a
=> [10, 2, 3, 4]
irb> a.object_id
=> 70284299180520
# hashの場合
irb> def baz x, y
irb> x.store "fourth", y
irb> x[:first] = 10
irb> end
=> :baz
irb> hash = {first: 1, second: 2, third: 3}
=> {:first=>1, :second=>2, :third=>3}
irb> baz hash, 4
=> 10
irb> hash
=> {:first=>10, :second=>2, :third=>3, "fourth"=>4}
|
上記のコードを見てみるとarrayの場合はarray変数の0番目indexの値が変わり、端っこにデータが追加されたのが確認できる。hashの場合もfirst
のオブジェクトが変わり、端っこにデータが追加されたのが確認できる。
追記
オブジェクトはどうなるかについても試してみたので追記。
下記のようなクラスを作成する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class Name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def first_name(name)
@first_name = name
@first_name
end
def last_name(name)
@last_name = name
@last_name
end
def full_name
full_name = "#{@last_name} #{@first_name}"
end
end
|
Name
のオブジェクトを作ってfull_name
のメソッドを実行すると
1
2
3
4
| irb> create_name = Name.new "Nobunaga", "Oda"
=> #<Name:0x007f8483c1f8b8 @first_name="Nobunaga", @last_name="Oda">
irb> create_name.full_name
=> "Oda Nobunaga"
|
普通に入力した通りOda Nobunaga
というフルネームが取得される。
ここで下記のようなクラスを作成して試してみる。
1
2
3
4
5
6
7
8
9
10
| class Change
def initialize(obj)
@name_obj = obj
end
def change_str
@name_obj.first_name("hogehoge")
@name_obj.last_name("foo")
end
end
|
このChange
クラスにcreate_name
オブジェクトを渡して実装してみると
1
2
3
4
5
6
7
8
9
10
| irb> create_name = Name.new "Nobunaga", "Oda"
=> #<Name:0x007f8483c1f8b8 @first_name="Nobunaga", @last_name="Oda">
irb> create_name.full_name
=> "Oda Nobunaga"
irb> change = Change.new create_name
=> #<Change:0x007f84838aae58 @name_obj=#<Name:0x007f84838c0898 @first_name="Nobunaga", @last_name="Oda">>
irb> change.change_name
=> "foo"
irb> create_name.full_name
=> "foo hogehoge"
|
create_name.full_name
を叩いた時に値が変わることが確認できる。
では、create_name
とChange
クラスの@name_obj
の変数が同じものなのかな。確認してみよう。
1
2
3
4
5
6
7
8
9
10
11
12
| class Change
attr_accessor :name_obj
def initialize(obj)
@name_obj = obj
end
def change_name
@name_obj.first_name("hogehoge")
@name_obj.last_name("foo")
end
end
|
Change
クラスにattr_accessor
を追加して@name_obj
を使えるようにする。
1
2
3
4
5
6
7
8
9
10
11
12
| irb> create_name = Name.new "Nobunaga", "Oda"
=> #<Name:0x007f8483b03740 @first_name="Nobunaga", @last_name="Oda">
irb> change = Change.new(create_name)
=> #<Change:0x007f8483ae06c8 @name_obj=#<Name:0x007f8483b03740 @first_name="Nobunaga", @last_name="Oda">>
irb> create_name === change.name_obj
=> true
irb> create_name.equal? change.name_obj
=> true
irb> create_name.object_id
=> 70103560887200
irb> change.name_obj.object_id
=> 70103560887200
|
create_name
と@name_obj
は同じものだったそうだ。
ということで、オブジェクトも参照の値渡しということが分かってきた。
参照
お気楽 Ruby プログラミング入門
Rubyの関数の引数は値渡しであることの証明をする
メソッドの引数(値渡し、参照渡し)