Ruby では Dir.chdir とか Kernel#open, Mutex#synchronize のように、ブロックを渡してそのブロックの呼び出しの前後に準備/後片付けの処理で挟むためのメソッドというのがありますね。ブロックから抜けると自動的に後始末をしてくれるので便利です。
ブロックの中でそのようなメソッドをネストさせることもできます。
@mutex.synchronize do open(file) do ... end end
そこで複数のオブジェクトについてそのようなブロックつきメソッドをネストさせて呼びだしたい、しかもオブジェクトの数は可変個(たとえば配列に入ってくる)にしたいという時にどう書くのか考えてみました。思ったよりも難しかったです。
class A def initialize(n) @n = n end def guard(&blk) begin p [:before, @n] blk.call ensure p [:after, @n] end end end ary = Array.new(5){|i| A.new(i) } enum = ary.to_enum callback = lambda do begin enum.next.guard(&callback) rescue StopIteration p :inner end end callback.call
実行結果はこんな風になります。
[:before, 0] [:before, 1] [:before, 2] [:before, 3] [:before, 4] :inner [:after, 4] [:after, 3] [:after, 2] [:after, 1] [:after, 0]
最初にこの問題を考えついた時には直感的に継続(callcc とか Fiber とか)が使えるんじゃないかと思いましたが、あまりうまい使い方が思いつきませんでした。to_enum でEnumerator を作っているので継続を使っているといえば使ってはいますが、そこは別に ary.shift しても良いので本質的ではないです。
もっといい書き方があるんじゃないかと思うのですが、思いついたら教えてください。