From: SASADA Koichi Date: 2008-05-19T19:03:10+09:00 Subject: [ruby-dev:34753] Re: block_given? in defined method  ささだです. Yukihiro Matsumoto wrote: > | block_given? は,多分 yield やら Proc.new などが出来るかどうかを確 > |認するためのものだと思うのですが,define_method で定義したメソッドで > |はどちらも出来ません(現状では).なので,block_given? も,その用途 > |には使えない以上,テストケースのようにするのはまずいと思うのですが, > |いかがでしょうか. > > えーと、できないんでしたっけ。それを認識していませんでした。 > lambdaはブロック受け付けますよね。で、define_methodで定義され > たメソッドではできないと言うのは実装上の深い理由があるんでしょ > うか。  lambda と define_method で定義したメソッド(以下,dm)は同程度にブ ロックを受け付けます. l = lambda{|&b| p b yield #=> # } class C define_method(:dm){|&b| p b yield #=> # } end begin l.call{} rescue LocalJumpError => e p e end begin C.new.dm{} rescue LocalJumpError => e p e end  YARV では yield の定義を,新しい変数スコープを作成するタイミングで 渡されたブロック情報を利用して行う新しいスコープの作成・実行としてい ます.define_method で定義されたブロックは,呼び出しても新しいスコー プは作らないため(ブロックの外のスコープを引き継ぐため),dm に渡し たブロックは対象にできません.ただし,ブロックを block 引数(&b)で 受けることが出来るので,それを使おうという方針だったと思います.  このルールは,結局ほかのブロックと同じなので,わかりやすいと言える かと思います.つまり,以下のような例では,setup に渡したブロックが呼 ばれますが,これは字面上,他のブロックと lambda,define_method 等で 同じ挙動です. class C end def setup $l = lambda{|&b| p b yield #=> block in main } C.module_eval{ define_method(:dm){|&b| p b yield #=> block in main } } end setup{ puts "block in main" } $l.call{} C.new.dm{}  define_method を特別扱いするのなら,最初から define_method を意識 した作りにしないと速度的に難しいかな,と思います.速度を気にしないの なら,もしかしたらそんなに難しくないかもしれません. # たしか,このあたりは去年,無理です,勘弁して下さい, # と言っていたような気がする. > | この場合,どちらも false になるべきなような気がします. > > 第一の選択肢はdefine_methodで定義されたメソッドからもyieldで > きてほしいんですが、なんらかの事情でそれが可能でないならば、 > その場合は両方falseでも良いと思います。  上記の理由で納得してもらえるのなら false にあわせるようにします. そうでなく,やはり define_method は特別扱いしろ,ということだった ら,もう少し考えさせて下さい. -- // SASADA Koichi at atdot dot net