ごとけんです

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"))