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

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

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

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

Q&A

解決済

2回答

892閲覧

Rubyのeachメソッドを独自に定義する場合について

sachatete

総合スコア18

Ruby

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

0グッド

0クリップ

投稿2018/09/13 11:09

前提・実現したいこと

こんばんは。

入れ子になったインスタンスに対してeach, rev_eachメソッドを呼び出すことで、各インスタンスが持つ**@valの値を取り出したいのですが、以下のコードでrev_each**が動く仕組みが分かりません。

@link.rev_each(&blk) if @linkで**@val = 5を持つLinkedListのインスタンスまで降りていくことで、eachメソッドとは逆に@valの値を5から取り出すことができるという点までは分かるのですが、なぜ@val = 5のインスタンスまで潜ったあとに@val = 4**のインスタンスのインスタンス変数を引数にしてブロックを実行できるのでしょうか?
@val = 5のインスタンスまで潜った後に、ブロックを実行してメソッドが終了するように見えるのですが、継続して入れ子のインスタンスを遡ることができる仕組みを教えて頂きたいです。

よろしくお願いします。

該当のソースコード

class LinkedList def initialize(val) @val = val end def <<(val) @link = LinkedList.new val end def each(&blk) blk.call(@val) @link.each(&blk) if @link end def rev_each(&blk) @link.rev_each(&blk) if @link blk.call(@val) end end ll = LinkedList.new(1) ll << 2 << 3 << 4 << 5 pp ll # => #<LinkedList:0x0000000006a9e128 # @link= # #<LinkedList:0x0000000006b7f8d0 # @link= # #<LinkedList:0x0000000006b7f880 # @link= # #<LinkedList:0x0000000006b7f858 # @link=#<LinkedList:0x0000000006b7f830 @val=5>, # @val=4>, # @val=3>, # @val=2>, # @val=1> ll.each{|el| p el} # => 1 # 2 # 3 # 4 # 5 ll.rev_each{|el| p el} # => 5 # 4 # 3 # 2 # 1

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

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

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

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

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

guest

回答2

0

どちらも再帰呼び出しによる実装ですね。
再帰呼び出しは、行きと戻りの両方があるので、ややこしいです。

具体的にどう動くかというと、こんな感じです。

同じメソッドですが、receiverとなるLinkedListのインスタンスが異なります。

# 1回目 def rev_each(&blk) @link.rev_each(&blk) if @link # [2,3,4,5]を評価してから = 2回目の評価を先に行う ... end # 2回目 def rev_each(&blk) @link.rev_each(&blk) if @link # [3,4,5]を評価してから = 3回目の評価を先に行う ... end # 3回目 def rev_each(&blk) @link.rev_each(&blk) if @link # [4,5]を評価してから = 4回目の評価を先に行う ... end # 4回目 def rev_each(&blk) @link.rev_each(&blk) if @link # [5]を評価してから = 5回目の評価を先に行う ... end # 5回目 def rev_each(&blk) @link.rev_each(&blk) if @link # 末端なので何もしない blk.call(@val) # 5を評価する end # 4回目の評価の続き def rev_each(&blk) @link.rev_each(&blk) if @link # 5回目の評価をしてきた = 5 blk.call(@val) # 次に4を評価する end # 3回目の評価の続き def rev_each(&blk) @link.rev_each(&blk) if @link # 4回目の評価をしてきた = 5,4 blk.call(@val) # 次に3を評価する end # 2回目の評価の続き def rev_each(&blk) @link.rev_each(&blk) if @link # 3回目の評価をしてきた = 5,4,3 blk.call(@val) # 次に2を評価する end # 1回目の評価の続き def rev_each(&blk) @link.rev_each(&blk) if @link # 2回目の評価をしてきた = 5,4,3,2 blk.call(@val) # 最後に1を評価する end

上手く伝わればよいのですが…
eachが再帰呼び出しの「行き」で処理を噛ませているのに対して、
rev_eachが「帰り」に処理を噛ませているので逆順になるのです。

投稿2018/09/13 12:19

編集2018/09/13 12:21
takumiabe

総合スコア661

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

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

sachatete

2018/09/13 13:26

ご回答ありがとうございます。 なぜeach, rev_eachでblk.call(@val)が違う位置で使われているのか分からなかったのでスッキリしました!
guest

0

ベストアンサー

再帰呼び出ししたメソッドは、端(@link==nil)まで行くと、リターン、リターン・・・で元のノードに帰ります。
eachrev_eachの違いは、リンクをたどっていく行く道で処理をするか、リンクをたどった帰り道で処理するかの違いです。

本題とは関係ないですが、initializeの中で、@link = nilがあった方が良いですね。インスタンス変数は未代入でも参照できてnilなので、実害は無いのですが。

##追記

簡単な例を示して頂くことはできますか...?

行く道、帰り道の例ですか?

Ruby

1puts "階乗の計算( n! = n * (n-1)! )" 2def factorial(n) 3 return 1 if n == 1 4 n * factorial(n-1) 5end 6puts factorial(5) 7 8puts "同じく。行く道で出力" 9def factorial_go(n) 10 return 1 if n == 1 11 puts "これから #{n-1} の階乗計算を呼び出す" 12 f = factorial_go(n-1) 13 n * f 14end 15puts factorial_go(5) 16 17puts "同じく。帰り道で出力" 18def factorial_back(n) 19 return 1 if n == 1 20 f = factorial_back(n-1) 21 puts "ちょうど #{n-1} の階乗計算から戻ったところ" 22 n * f 23end 24puts factorial_back(5)

投稿2018/09/13 12:08

編集2018/09/13 16:14
otn

総合スコア84423

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

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

sachatete

2018/09/13 13:23

ご回答ありがとうございます。 再帰呼び出しというものを知りませんでした。 確かに、initializeの中で@linkを使うことを示しておいたほうがわかりやすいですね。 再帰呼び出ししたメソッドが元のノードに戻る仕組みを教えて頂きたいです!
otn

2018/09/13 13:29

別に再帰呼び出しでなくても、メソッドを呼び出して、そのメソッドが終われば、呼び出した次の命令に戻ってきます。 親を処理中のメソッドから、子を処理するメソッドを呼んで、そのメソッドが終われば、親を処理中のメソッドに帰ってきます。
sachatete

2018/09/13 14:11

簡単な例を示して頂くことはできますか...?
sachatete

2018/09/17 08:18

理解することができました、ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問