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

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

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

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

Q&A

解決済

3回答

267閲覧

再帰のコードで何が起きているのかわからなくなってしまいます。

tuvalu

総合スコア136

Ruby

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

1グッド

1クリップ

投稿2018/04/12 13:39

編集2018/04/13 08:06

ただいまRubyで再帰を理解しようとしています。(後で追加:順列の列挙(検索用))
でも、途中でで何が起きているのかわからなくなってしまいます。
コード自体が切り貼りで間違っているかもしれません。。おかしな勘違いをしているかもしれません。
よろしければ教えてください。よろしくお願い致します。

Ruby

1def permutation(ary, new_ary = []) 2 p new_ary if ary.length == new_ary.length 3 4 ary.each do |n| 5 next if new_ary.member? n 6 new_ary.push n 7 permutation(ary, new_ary) 8 new_ary.pop 9 end 10end 11 12permutation([1,2,3]) 13結果 14[1, 2, 3] 15[1, 3, 2] 16[2, 1, 3] 17[2, 3, 1] 18[3, 1, 2] 19[3, 2, 1] 20

うまく動いているようです。が、どう動いてこうなるのかを知りたく、
next if p new_ary.member? n と見えるようにして、あと
p new_ary.push n
p new_ary.pop と見えるようにしました。

Ruby

1結果(長くてすみません) 2false 3[1] 4true 5false 6[1, 2] 7true 8true 9false 10[1, 2, 3] 11[1, 2, 3] 12true 13true 14true 153 162 17false 18[1, 3] 19true 20false 21[1, 3, 2] 22[1, 3, 2] 23true 24true 25true 262 27true 283 291 30false 31[2] 32false 33[2, 1] 34true 35true 36false 37[2, 1, 3] 38[2, 1, 3] 39true 40true 41true 423 431 44true 45false 46[2, 3] 47false 48[2, 3, 1] 49[2, 3, 1] 50true 51true 52true 531 54true 55true 563 572 58false 59[3] 60false 61[3, 1] 62true 63false 64[3, 1, 2] 65[3, 1, 2] 66true 67true 68true 692 70true 711 72false 73[3, 2] 74false 75[3, 2, 1] 76[3, 2, 1] 77true 78true 79true 801 81true 82true 832 84true 853 86

これを見ると、最初に引数aryの配列の1番目が、変数new_aryにあるか見て、無いのでnew_aryに代入しています。
次に再帰でメソッドpermutation([1,2,3], [1])を呼んでいるものと理解しています。

2回目のeachで、今度は引数aryの配列の1番目が、変数new_aryにあるので、次のnを評価して、これまたないので、new_aryに2を代入。
次に再帰でメソッドpermutation([1,2,3], [1,2])を呼んでいるものと理解しています。

3回目のeachで、今度は引数aryの配列の1番目も2番目も変数new_aryにあるので、次のnを評価して、new_aryに3を代入。
次に再帰でメソッドpermutation([1,2,3], [1,2,3])を呼んでいるものと理解しています。

そのあとから急にわからなくなってしまいます。表示されている結果は
true
true
true
ここまではまだそうだなあと思えるのですが、次に
3
2
false
[1,3]
となり、もうわからなくなってしまいました。
しまいにはpermutationに再帰するのなら、new_ary.popの出番がないのでは、とも
思うようになってしまいました。頭から湯気が出ています。
もし助けて頂ける方がいらっしゃいましたら、どうぞ、よろしくお願い致します。

HayatoKamono👍を押しています

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

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

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

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

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

guest

回答3

0

理解を助けるためにちょいとソース弄って無駄を排除およびデバッグ出力を追加してみました

ruby

1def permutation(ary, new_ary = [], level = 0) 2 return puts " " *level + "OUT=> #{new_ary.inspect}" if ary.length == new_ary.length 3 puts " " *level + "Enter #{new_ary.inspect}" 4 5 (ary - new_ary).each do |n| 6 new_ary.push n 7 permutation(ary, new_ary, level + 1) 8 new_ary.pop 9 end 10 puts " "*level + "Leave #{new_ary.inspect}" 11end 12 13permutation([1,2,3])

結果は

Enter [] Enter [1] Enter [1, 2] OUT=> [1, 2, 3] Leave [1, 2] Enter [1, 3] OUT=> [1, 3, 2] Leave [1, 3] Leave [1] Enter [2] Enter [2, 1] OUT=> [2, 1, 3] Leave [2, 1] Enter [2, 3] OUT=> [2, 3, 1] Leave [2, 3] Leave [2] Enter [3] Enter [3, 1] OUT=> [3, 1, 2] Leave [3, 1] Enter [3, 2] OUT=> [3, 2, 1] Leave [3, 2] Leave [3] Leave []

投稿2018/04/13 05:21

asm

総合スコア15147

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

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

tuvalu

2018/04/13 07:55

たいへんありがとうございます。助かります。
guest

0

Python Tutor
こちらで挙動を追うとわかりやすいと思います。

投稿2018/04/12 23:12

編集2018/04/12 23:14
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

tuvalu

2018/04/12 23:21

ありがとうございます。 見たことない画面で、まだ使い方がわかりませんがおもしろそうです! 今後も使えますか? ごめんなさい。ベストアンサーは昨夜ご指導頂いた方にしています。 今後ともよろしくお願い致します。失礼します。
tuvalu

2018/04/13 02:30

大変恐縮ですがよろしいでしょうか? 教えて頂いたサイトで一手づつ順を追って理解していますが、 45手めで一巡してループ処理が正常に終了するのに、46手めで急にループの中の半ばにある 再帰を呼び出す(呼び出せる)のですか? たぶんここが急にわからなくなってしまうポイントかと思われました。 ここがわかれば次に進めると思います。ご教授ください。よろしくお願い致します。
defghi1977

2018/04/13 02:33

再帰処理として呼び出した permutation(ary, new_ary) が完了したので, 次の行に処理が移っただけだよ
guest

0

ベストアンサー

rubyは知りませんが, 大体の内容は把握できました.

Ruby

1def permutation(ary, new_ary = []) 2 p new_ary if ary.length == new_ary.length 3 4 ary.each do |n| #(a) 5 next if new_ary.member? n 6 new_ary.push n 7 permutation(ary, new_ary) 8 new_ary.pop 9 end 10end

(a)のループはかいつまんで言うと, 配列arrの中でまだ選ばれていない(new_arrに存在しない)の値のみで繰り返されるループです.
NOTE:new_ary.popは呼び出し元に影響を及ぼさないようにするための後処理です.

従って全体の処理の流れとしてはまず大本の(a)ループが発生するため,

  • 最初に1を選んだケース
  • 最初に2を選んだケース
  • 最初に3を選んだケース

となります.

で, (a)のループは再帰処理により(a)のループを呼び出しますが, その際既に選ばれているものについてはループが除外されるため…

  • 最初に1を選んだケース
    • 二番目に2を選んだケース
    • 二番目に3を選んだケース
  • 最初に2を選んだケース
    • 二番目に1を選んだケース
    • 二番目に3を選んだケース
  • 最初に3を選んだケース
    • 二番目に1を選んだケース
    • 二番目に2を選んだケース

の順に処理が行われることになります. また, (a)のループは再帰処理により(a)のループを呼び出すため,

  • 最初に1を選んだケース
    • 二番目に2を選んだケース
    ーー 三番目に3を選んだケース
    • 二番目に3を選んだケース
    ーー 三番目に2を選んだケース
  • 最初に2を選んだケース
    • 二番目に1を選んだケース
    ーー 三番目に3を選んだケース
    • 二番目に3を選んだケース
    ーー 三番目に1を選んだケース
  • 最初に3を選んだケース
    • 二番目に1を選んだケース
    ーー 三番目に2を選んだケース
    • 二番目に2を選んだケース
    ーー 三番目に1を選んだケース

の順に処理が行われることになります.

さて, この三段目のループで再帰処理が呼び出されると, 関数の一行目の条件に合致することからnew_aryの内容・つまりarrの中の要素の選んだ順が出力されることになるので...

  • 最初に1を選んだケース
    • 二番目に2を選んだケース
    ーー 三番目に3を選んだケース>>>[1,2,3]
    • 二番目に3を選んだケース
    ーー 三番目に2を選んだケース>>>[1,3,2]
  • 最初に2を選んだケース
    • 二番目に1を選んだケース
    ーー 三番目に3を選んだケース>>>[2,1,3]
    • 二番目に3を選んだケース
    ーー 三番目に1を選んだケース>>>[2,3,1]
  • 最初に3を選んだケース
    • 二番目に1を選んだケース
    ーー 三番目に2を選んだケース>>>[3,1,2]
    • 二番目に2を選んだケース
    ーー 三番目に1を選んだケース>>>[3,2,1]

の順番に配列の内容が出力されたのです.

投稿2018/04/12 14:48

defghi1977

総合スコア4756

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

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

tuvalu

2018/04/12 22:58

先日と同様、早々のご回答ありがとうございます! 前回よりも難しいと思っています。 ゆっくりと咀嚼し、しっかり消化させていただきます。m__m
tuvalu

2018/04/13 02:40

大変恐縮ですがよろしいでしょうか? defghi1977さんに教えて頂いた内容と、出力結果と、あと先ほどte2jiさんに教えて頂いたサイトで一手づつ三つ巴で順を追っていますが、そのページ(https://goo.gl/R67dSo)の実行展開のなかで 45手めで一巡してループ処理が正常に終了するのに、46手めで急にループの中の半ばにある 再帰を呼び出す(呼び出せる)ところが、たぶんここが急にわからなくなってしまうポイントかと思われます。 ここがわかれば次に進めると思います。ご教授ください。よろしくお願い致します。失礼いたします。
tuvalu

2018/04/13 02:56 編集

ループが終わってから改めて呼んでいるのではなく、再帰処理が終わったから その続きでそこから次の行に移った、という訳ですね。 わかってきた気がしてきました。 度々のご回答ありがとうございます。
tuvalu

2018/04/13 02:58

これで質問のなかにあった、 しまいにはpermutationに再帰するのなら、new_ary.popの出番がないのでは、とも思うようになってしまいました。 というところもクリアできそうです!
tuvalu

2018/04/13 03:37

ループのなかにある再帰文より上に記述されている処理が再帰処理中のループ処理、 そして再帰文より下に記述されている処理が再帰処理が終わったあとのループ処理、 ということで、よろしかったでしょうか?
defghi1977

2018/04/13 03:46

なんか言ってる意味が判らんけれど. ループに拘ると見えてるものも見えなくなると思うよ. 再帰処理は「似た構造の繰り返し」に用いるテクニックで, 今回は{1, 2, 3}というデータに対し, その各ノードに{1, 2, 3}がぶら下がっていて, 更にその下にも{1, 2, 3}がぶら下がっているデータ構造を(ループと再帰で)順に辿っているというのがミソなんだ. コレに似た構造としてはディレクトリ構造やDOMツリー等が考えられるでしょう.
tuvalu

2018/04/13 04:42

なんかわかった気になって喜んでいたのですが、どうも勘違いのようです。 いろいろ教えていただきありがとうございました。 少し休憩します。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問