ごとけんです In message "[ruby-list:28380] Re: 【要望】 EmacsLisp みたいなフック" on 01/02/28, kai13 / basil.freemail.ne.jp <kai13 / basil.freemail.ne.jp> writes: >実装について、フックと表現しましたが、 >EmacsLispではhookを利用しても、モードなどの作成に挫折したクチですので >あまり厳密にとらえないでください(^^;。 厳密というか、それをフックと呼ぶのは誤解の素と思いました。 >begin # beginブロック > #beginブロック(a) >hook > #hookブロック >end 要するに、複数のブロックが渡したいんですよね。たとえば、以下 のような方法があります。 (どこかで聞いたような話ですが)複数のブロックを渡したい場合の 例として、2分木を深さ優先で「なぞる」(traverseする)ときがあ ります。木をなぞりながらブロックを実行させるには、次の3つの タイミングがあります: * あるノードに到達したとき * そのノードの左の枝の処理が終わったとき * そのノードの両方の枝の処理が終わったとき これらを次のように指定できるようにすることが可能です。 BinTree.traverse do |v| v.before do |node| ... end # 達した v.middle do |node| ... end # 左が終わった v.after do |node| ... end # 両方終わった end これは traverse が2段階の動作をすることで実現されます。 (1) 指定される「なぞり屋」 v をつくる。その動作の指定は ブロックで与える (2) v になぞらせる ポイントは、ブロックパメータが要素をあらわす必要はないという ことです。この場合は v がそうです。 # ちなみに、ぼくは以前、中田さんに Benchmark のパッチをもらっ # たときに初めてこのことを理解しました。 -- Gotoken # 参考までに、一つの実装例を示します。木の表示です:
class BinTree BEFORE, MIDDLE, AFTER = :before, :middle, :after def initialize(data, left = nil, right = nil) @data, @left, @right = data, left, right end def traverse(&block) traverse0(BinTree::Visitor.new(&block)) end protected def traverse0(visitor) visitor.execute BEFORE, @data @left.traverse0 visitor if @left visitor.execute MIDDLE, @data @right.traverse0 visitor if @right visitor.execute AFTER, @data end class Visitor def initialize yield(self) end def before(&block) @before_proc = block end def middle(&block) @middle_proc = block end def after(&block) @after_proc = block end def execute(timing, data) case timing when BEFORE @before_proc.call data if @before_proc when MIDDLE @middle_proc.call data if @middle_proc when AFTER @after_proc.call data if @after_proc end end end end # example def BinTree(data, left = nil, right = nil) BinTree.new(data, left, right) end t = BinTree("red", BinTree("blue", BinTree("yellow"), BinTree("pink")), BinTree("green")) indent = " "*8 lv = 0 t.traverse do |v| v.before do |i| print ",\n" if lv > 0 print indent*lv, "BinTree(#{i.inspect}" lv += 1 end v.after do |i| print ")" lv -= 1 print "\n" if lv.zero? end end __END__ # produces: BinTree("red", BinTree("blue", BinTree("yellow"), BinTree("pink")), BinTree("green"))