From: nobu@... Date: 2005-02-26T21:05:30+09:00 Subject: [ruby-dev:25772] behavior なかだです。 [ruby-dev:24302]の野分さんの案について考えていたんですが、Proc とは別でいいんじゃないかと思えてきました。 元の関数呼出しの話とは離れますが、かわりにこういうものを思い付 きました。 format = "unknown".behaving(:%) {|s| s ? "#{s}" : self} p format % "hoge" p format % nil hoge = Object.new.behaving(to_s: proc{"hoge"}, to_i: proc{42}) printf "%s:%d\n", hoge, hoge rubyでの実装はこんな感じです。 class Behavior < Module def initialize(behavior, &body) if body define_method(behavior, &body) else behavior.each do |behavior, body| if body define_method(behavior, &body) elsif body.nil? remove_method(behavior) else undef_method(behavior) end end end end end module Kernel def behaving(behavior, &body) extend(Behavior.new(behavior, &body)) end end Cで実装するならこういう感じ。 * eval.c (behavior_initialize): new class Behavior. * eval.c (rb_behaving): make the receiver to behave as the given proc. Index: eval.c =================================================================== RCS file: /cvs/ruby/src/ruby/eval.c,v retrieving revision 1.755 diff -U2 -p -r1.755 eval.c --- eval.c 22 Feb 2005 14:57:43 -0000 1.755 +++ eval.c 26 Feb 2005 12:02:17 -0000 @@ -9407,4 +9407,78 @@ rb_mod_define_method(argc, argv, mod) } +VALUE rb_cBehavior; + +static VALUE +define_behavior(beh, mod) + VALUE beh, mod; +{ + VALUE args = rb_check_array_type(beh); + VALUE body = Qnil; + ID name; + + if (!NIL_P(args)) { + name = rb_to_id(rb_ary_entry(args, 0)); + body = rb_ary_entry(args, 1); + } + else { + name = rb_to_id(args); + } + if (!body) { + rb_add_method(mod, name, 0, NOEX_UNDEF); + } + else if (NIL_P(body)) { + remove_method(mod, name); + } + else { + VALUE arg[2]; + arg[0] = ID2SYM(name); + arg[1] = body; + rb_mod_define_method(2, arg, mod); + rb_export_method(mod, name, NOEX_PUBLIC); + } + return Qnil; +} + +/* + * call-seq: + * Behavior.new(symbol) { block } => behavior + * Behavior.new(name: proc { block }) => behavior + * + * Define singleton methods to the given instance. + */ + +static VALUE +behavior_initialize(self, behavior) + VALUE self, behavior; +{ + if (rb_block_given_p()) { + VALUE arg[2]; + arg[0] = behavior; + arg[1] = proc_lambda(); + rb_mod_define_method(2, arg, self); + rb_export_method(self, rb_to_id(behavior), NOEX_PUBLIC); + } + else { + rb_iterate(rb_each, behavior, define_behavior, self); + } + return self; +} + +/* + * call-seq: + * obj.behaving(symbol) { block } => obj + * obj.behaving(name: proc { block }) => obj + * + * Defines the given block as the singleton method of the +obj+. + */ + +static VALUE +rb_behaving(self, behavior) + VALUE self, behavior; +{ + behavior = behavior_initialize(rb_obj_alloc(rb_cBehavior), behavior); + return rb_obj_extend(1, &behavior, self); +} + /* * Proc objects are blocks of code that have been bound to @@ -9489,4 +9563,8 @@ Init_Proc() rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); rb_define_method(rb_cModule, "instance_method", rb_mod_method, 1); + + rb_cBehavior = rb_define_class("Behavior", rb_cModule); + rb_define_method(rb_cBehavior, "initialize", behavior_initialize, 1); + rb_define_method(rb_mKernel, "behaving", rb_behaving, 1); } -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦