質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

4回答

1987閲覧

rubyのArrayのコンストラクタへの要素の渡し方

mitsuru793

総合スコア157

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

1グッド

1クリップ

投稿2015/08/19 05:51

RubyのArrayについて質問があります。

下記の失敗例(コード)の要素が全て{:cat=>"Nuko"}で埋まる理由は、1つのHashインスタンスへの参照がaryのすべての要素に代入されているからだと思います。つまり、Hash.newは一度しか行われていません。

成功例の方は、3回Hash,newが実行され、各Hashインスタンスがaryの要素に代入されているため、個別の要素ごとに値を代入できます。

ruby

1#失敗 2ary = Array.new(3, Hash.new) 3ary[0][:cat] = "Nuko" 4ary 5# => [{:cat=>"Nuko"}, {:cat=>"Nuko"}, {:cat=>"Nuko"}]

ruby

1#成功 2ary = Array.new(3){ Hash.new } 3ary[0][:cat] = "Nuko" 4ary 5# => [{:cat=>"Nuko"}, {}, {}]

ですが、失敗例のコンストラクタにbooleanのtrueを渡したところを、成功例と同じ現象が起きました。どうやら参照がわたっているわけではないようです。

ruby

1#失敗例と同じ生成 2ary = Array.new(3, true) 3ary[1] = false 4ary 5# => [true, false, true]

もしかしたらrubyはJavaScriptやPythonのように全てがオブジェクトでできているわけでなく、Javaのようにプリミティブ値があるのかと思いました。

どうしてArray.new(3, Hash.new)だと1つのHashインスタンスしか代入されないのに、Array.new(3, true)だと3つのtrueが代入されるのでしょうか?

ご回答、よろしくお願い致します。

DrqYuto👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ruby

1ary[1] = false

上記の代入文は、「ary[1]の参照先の値」ではなく「ary[1]がどこを参照するか」を書き換えています。
よって、ary[0]やary[2]の参照先の値に影響を与えることはありません。

ハッシュの方は、「ary[1]の参照先の値が持つ要素」を書き換える操作になっているので、動作に差が出ているのではないでしょうか。

投稿2015/08/19 06:18

hy3

総合スコア594

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mitsuru793

2015/08/19 07:29

Hashは参照先を変更しているわけではなく、同じ参照先の値を変更しているのですね。 ご回答頂き、ありがとうございます。
guest

0

以下の 3 例を比べればわかるように、別に Hash だから、true, false, nil だからというものでもないと思います。
ちなみに重要なのは String#<< は自己破壊的(同じオブジェクトの末尾に追加している) String#+ は自己破壊的でない (末尾に文字列を追加した「別の」オブジェクトを返す) ことです。

ruby

1ary = Array.new(3, "str") 2ary 3# => ["str", "str", "str"]

ruby

1ary = Array.new(3, "str") 2ary[1] = "other" 3ary 4# => ["str", "other", "str"]

ruby

1ary = Array.new(3, "str") 2ary[1] += "_tail" 3ary 4# => ["str", "str_tail", "str"]

ruby

1ary = Array.new(3, "str") 2ary[1] << "_tail" 3ary 4# => ["str_tail", "str_tail", "str_tail"]

投稿2015/08/19 07:23

H_Kuruno

総合スコア65

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/08/19 07:49

勉強になりましたー!
mitsuru793

2015/08/19 08:04

上記のソースコードだと、aryのすべての要素に同じ文字列への参照が入っていることになりますね。 回答だけでなく、ソースコードまで記述して頂き、ありがとうございます。
guest

0

hy3さんの意見に賛成です。ちょっとだけ補足させてください。

Ruby

1ary[0][:cat] = "Nuko"

は、ハッシュオブジェクトにペア(:cat,"Nuko")を追加しています。

Ruby

1ary[0] = 1

は、aryの最初を1というFixnumクラスのオブジェクトで置き換えています。

もしかしたらrubyはJavaScriptやPythonのように全てがオブジェクトでできているわけでなく、Javaのようにプリミティブ値があるのかと思いました。

Rubyは全てオブジェクトで出来ています。試しにrubyの対話環境等で下記を実行してみてください。

ruby

1nil.class #=> NilClass 2true.class #=> TrueClass 3'foo'.class #=> String 4100.class #=> Fixnum 50x1a.class #=> Fixnum 60b11010.class #=> Fixnum 7123.4.class #=> Float 81.234e2.class #=> Float

投稿2015/08/19 06:44

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2015/08/19 07:20

ハッシュでも、ary[0] = {cat: 'Nuko'}とすればハッシュ自体が入れ替わるので残り2つには影響しませんからね。
mitsuru793

2015/08/19 08:16

失敗例のコードでaryの要素のobject_idを確認してみました。 ary[0].object_id 3.times { |i| p ary[i].object_id} 70336028860920 70336028860920 70336028860920 すべての要素には同じHashオブジェクトが入っていました。ary[0],ary[1],ary[2]どの要素に、ary[i][:cat] = "Nuko"を行っても同じHashオブジェクトにペア(:cat,"Nuko")を追加しているから、aryのどの要素を見ても値が変化しているように見えたのですね。 この時に、maisumakunさんが仰っていたとおりにary[0] = {cat: 'Nuko'}とすれば、他の要素の中身を確認しても影響がないことを確認しました。これはary[0]に新しく生成したHashオブジェクトの参照を渡しているからですよね。つまり、この時に作られているHashオブジェクトは2つになります。 成功例のコードでもobject_idを確認したら、3つのHashオブジェクトが生成されていることがわかりました。この時はary[0] = {cat: 'Nuko'}とする必要はありません。 ary2 = Array.new(3){Hash.new} 3.times { |i| p ary2[i].object_id } 70336028701520 70336028701500 70336028701480 このようにになるのは正常なのでしょうか?Arrayに要素を全て違うオブジェクトで初期化したい場合のみ、Procオブジェクト(ブロック文)の引数を指定するという使い方をすればいいのでしょうか? 掲示していただいたクラスのコードを確認しました。クラスがあるということは、それはインスタンスであり、インスタンスであることはオブジェクトになりますね。 おかげさまでスッキリしました。kompiroさん、maisumakunさん、ありがとうございます。
guest

0

ruby

1ary = Array.new(3, true) 2ary[1] = false 3p ary 4ary[0] = false 5ary[1] = true 6ary[2] = nil 7p ary[0].object_id 8p ary[1].object_id 9p ary[2].object_id

不思議に思ったので自分でもやってみたところ
false true nil の3種類は特別?のようです
true 20
false 0
nil 8
というobject_idでした。
この状態で各indexを変更しても同じIDにはなならなかったので

ruby

1ary = Array.new(3, true) 2ary = Array.new(3) {true}

この2つは true false nil に限り同じようです

投稿2015/08/19 06:40

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mitsuru793

2015/08/19 07:38

私も同じobject_idになっていることを確認しました。次のコードだと全て20が出力されます。aryの全ての要素にあるtrueは同じインスタンスなのですね。おそらくtrueに他の値を保持する必要がないので、ムダを省くために真理値はシングルトンになっているかと思います。 ary = Array.new(3, true) p ary[0].object_id p ary[1].object_id p ary[2].object_id # => 20 またひとつ勉強になりました。 ご回答、並びに実験をして頂き、ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問