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

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

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

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

Q&A

解決済

1回答

1342閲覧

配列の要素に変更が反映される理由

tkshp

総合スコア174

Ruby

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

0グッド

1クリップ

投稿2021/03/19 11:10

編集2021/03/20 06:45

前提・実現したいこと

配列をループで回して要素に変更を加えると、要素の変更が反映されるものと反映されないものがあります。
その理由についてご教示お願いします。

試したこと

試したところ、コメントの通りの結果となりました。

Ruby

1 numbers = [0,1,2] 2 numbers.each{|number| 3 number += 10 4 } 5 p numbers # [0, 1, 2] 6 7 my_strings = ["A", "B", "C"] 8 my_strings.each{|my_string| 9 my_string += "_add" 10 } 11 p my_strings # ["A", "B", "C"] 12 13 hash_arr = [{"key"=>""}, {"key"=>""}] 14 hash_arr.each_with_index{|my_hash, index| 15 my_hash["key"] = "value" + (index+1).to_s 16 } 17 p hash_arr # [{"key"=>"value1"}, {"key"=>"value2"}]

この結果について、

配列の数値型やString型の要素は値型なので、ループの中での要素の変更が反映されない。 配列のハッシュの要素は参照型なので、ループの中での要素の変更が反映される (ハッシュは、参照型の変数で参照されている)。

という理由なのではないかと考えていますが、こちらの認識で合っていますか?
ご教示お願いします。

追記

C#でも試してみました。

C#

1 private void Form1_Load(object sender, EventArgs e) 2 { 3 int[] numbers = new int[3] { 0, 1, 2 }; 4 foreach (int number in numbers) 5 { 6 // number = 10; // エラー。foreachの繰り返し変数であるため、これに割り当てることはできない。 7 // number += 10; // エラー。foreachの繰り返し変数であるため、これに割り当てることはできない。 8 } 9 10 for (int i = 0; i<numbers.Length; i++) 11 { 12 if (i == numbers.Length - 1) 13 { 14 numbers[i] = 20; 15 } 16 else 17 { 18 numbers[i] += 10; 19 } 20 } 21 22 Console.WriteLine(String.Join(",", numbers)); // 10,11,30 23 24 int j = 1; 25 intAdd(j, 10); 26 Console.WriteLine(j); // 1 27 28 int[] arr = new int[2] { 0, 1 }; 29 arrAdd(arr); 30 Console.WriteLine(String.Join(",", arr)); // 5,6 31 32 } 33 34 void intAdd(int a, int b) 35 { 36 a += b; 37 } 38 39 void arrAdd(int[] arr) 40 { 41 arr[0] = 5; 42 arr[1] += 5; 43 }

追記②

Ruby

1 p hash_arr # [{"key"=>"value1"}, {"key"=>"value2"}] 2 3 hash_arr.each_with_index{|my_hash, index| 4 i = (index+1).to_s 5 my_hash.store("k"+i, "v"+i) 6 } 7 8 p hash_arr # [{"key"=>"value1", "k1"=>"v1"}, {"key"=>"value2", "k2"=>"v2"}]

C#

1 List<int> list = new List<int> { 1, 2, 3 }; 2 addList(list); 3 Console.WriteLine(String.Join(",", list)); // 1,2,3,4 4 5 void addList(List<int> list) 6 { 7 list.Add(4); 8 list = new List<int> { 0 }; 9 }

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

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

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

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

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

guest

回答1

0

ベストアンサー

惜しいですが、違います。

数値や文字列の+演算子は、元のオブジェクトと別の、「加算や連結した新たなオブジェクト」を生成します。その新たなオブジェクトを、ブロック引き数(ブロックローカル)に代入しているだけです。

ハッシュに対しての[]=メソッドは、レシーバーのハッシュオブジェクトを変更します(新たなオブジェクトを生成するのではない)。
イコールが含まれているので一見代入に見えますが、これは代入とは無関係なメソッド呼び出しです。通常の「レシーバー.メソッド名(引き数)」の形式で書くと、my_hash.[]=("key", "value" + (index+1).to_s)となります。

投稿2021/03/19 12:24

otn

総合スコア84788

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

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

tkshp

2021/03/19 18:07

ご回答ありがとうございます。 []=メソッドは、代入ではなくレシーバのハッシュオブジェクトを変更するものだったのですね。 理解できました。 正直、質問前までは、9割方、質問の認識で合っているだろうと思っていて、他の言語に関しても参照型の認識で覚えようと思っていたのですが、想定外の真実に驚きまして、他の言語、特にC#に関しても気になり、質問欄に追記したコードを試してみたのですが、結果はRubyのときと同じような結果で、 やはり参照型の配列の方は、ブロックスコープでも代入が反映されているように見える結果となったのですが、これもRubyのご回答いただいた内容と同様に、 特に参照型だからという理由ではなく、配列の[]=メソッドも代入ではなく、呼び出し元の配列を変更するメソッドだから反映される、ということでしょうか?
otn

2021/03/20 03:08

他の言語の建て付けはよく知らないのですが、 hoge[foo] = bar は、hogeという変数への代入ではなくhogeという変数が指し示すオブジェクトを加工するという意味では共通だと思います。 これに対して、 hoge = bar は、今まで変数hogeが指し示していたオブジェクトとhogeの関係を解消して、あらたに変数hogeは変数barが指し示すオブジェクトを指し示すようになると言う意味で、オブジェクトの加工ではないのもおそらく他の言語も同じでしょう。 hoge += foo は、Rubyでは hoge = hoge + foo と同じで、加工ではなく変数代入です。このあたりは言語により差があるかも知れません。
tkshp

2021/03/20 05:14

ご回答ありがとうございます。 とても勉強になりました。 ご教示いただきありがとうございました。
tkshp

2021/03/20 05:21

ベストアンサー後の質問ですみません、[]=の変数への代入ではなく、変数が指し示すオブジェクトを加工することに関して説明しているサイトを見てみたいと思っているのですが、どのようなサイトがありますか? もしくはどのような検索キーワードで検索できますか?
otn

2021/03/20 05:46

Rubyであれば、メソッドなので自明ですよね。 他の言語は、ちょっとわかりません。入門書の配列のところに書いてないですかね。
tkshp

2021/03/20 08:03

ご回答ありがとうございます。 度重なる質問ですみません。 ご回答やURLのご提示を受けて、さらに質問追記②のコードを試してみて、 考えをまとめてみました。 「値渡しによるブロックスコープ内での参照型の変数について、  戻り値で受け取らずに済む(=の代入で受け取らずに済む)メソッドを呼び出して変更の処理をした場合  (新たにオブジェクトを生成して返すのではなく、自身のオブジェクトを変更するメソッドを呼び出した場合)、  その変更内容は、ブロックスコープ外の参照元のオブジェクトにも反映される。  ただし、[]=メソッドは特殊で、=が付いているので一見、代入処理しているように見えるが、  実際は代入ではなく、元のオブジェクトを変更するものなので、  []=メソッドの処理も、ブロックスコープ外の参照元のオブジェクトに反映される。  =による代入は、ブロックスコープ内のローカル変数に対する代入なので、  ブロックスコープ外の参照元のオブジェクトに反映されない。  また、値型の変数の場合は、戻り値で受け取らずに済むメソッドで、自身を変更したとしても、  ブロックスコープ内の値型の変数は値のコピーなので、ブロックスコープ外の値型の変数には、  変更は反映されない。」 という考えに至ったのですが、間違っていないでしょうか? 断言はできないかもしれませんが、概ね合っていそう等、アドバイスをいただけましたら、 大雑把にこのように覚えたいと思っています。
otn

2021/03/20 08:58

概ね合ってると思います。 Rubyの場合は(建前としては)参照型と値型の区別が無く全てオブジェクト(参照型)です。 数値もオブジェクトですが、数値オブジェクトを変更するメソッドが存在しないので、 「引き数の数値を変更する」ということがそもそも出来ません。
tkshp

2021/03/20 09:48

ご回答ありがとうございます。 ブロックスコープ内の変数に関する処理についてとても理解が深まりました。 ご教示いただきありがとうございました。
otn

2021/03/20 12:23

なんかずれてる感があったのですが、「変数」と「オブジェクト」の混乱がまだあるのかも知れません。 あと、 > 値渡しによるブロックスコープ内での この「値渡し」が意味不明です。
tkshp

2021/03/23 14:30

コメントが遅くなり申し訳ございません。 いただいたコメントに気づかず、遅くなってしまいました。 オブジェクトは参照型の変数によって参照されているものと認識しています。 値渡しの件に関してですが、質問の説明不足ですみません。 ループ内のブロックスコープに限らず、関数のブロックスコープについても考えていて、 関数の引数は通常、値渡しなのでそのように表現しました (しかし、Rubyでは引数を参照渡しで渡す方法がないみたいということがわかりました。 Rubyの関数の引数は必ず値渡しになるようですね)。 ループに限定した内容で質問の仕方が間違っていました。 ループに限らず、関数などでも、ブロックスコープ内の変数の変更が、 ブロックスコープ外にも反映されるものと反映されないものの違いは何なのかという質問がしたく、 当初はそれが、変数が値型と参照型の違いによるものではないか?と認識していました。 しかし、 > Rubyの場合は(建前としては)参照型と値型の区別が無く全てオブジェクト(参照型)です。 こちらご教示いただき、理解しました。 今は、その変更が反映されるものというのは、 戻り値で受け取らずに済む(=の代入で受け取らずに済む)自身のオブジェクトを変更するメソッドを 呼び出した場合、反映されると認識しています。 メソッドによる変更が戻り値で受け取る(=の代入で受け取る)タイプだと、 ブロックスコープ内の参照変数の参照先が、その変更を処理したオブジェクトに変わるので、 ブロックスコープ外の元のオブジェクトには、変更が反映されないというふうにも認識しています。
otn

2021/03/23 14:45

> オブジェクトは参照型の変数によって参照されているものと認識しています。 間違ってはいないですが、Rubyだと参照型の変数しか無いです。 > Rubyの関数の引数は必ず値渡しになるようですね)。 一般的な言い方では、「参照の値渡し」だけです。 > 今は、その変更が反映されるものというのは、~~~ なんかやっぱり理解がおかしい気がしますね。 後半意味不明なので、うまく指摘できませんが。
tkshp

2021/03/23 15:50 編集

ややこしい説明ですみません。 最初にご教示いただいた = → 代入 []= → 破壊的メソッド この2点の違いは理解いたしました。 この差で、ブロックスコープ外の元のオブジェクトが変更されるかどうかの違いになると認識しています。 最初にご教示いただいた内容の繰り返しとなってしまってすみません。
otn

2021/03/23 15:57

その理解であれば合ってます。
tkshp

2021/03/23 15:59

ご回答ありがとうございます。 ご教示いただき、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問