首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

[转]eval, class_eval, instance_eval跟binding

2012-09-04 
[转]eval, class_eval, instance_eval和bindinghttp://www.cnblogs.com/rubylouvre/archive/2011/08/28/21

[转]eval, class_eval, instance_eval和binding
http://www.cnblogs.com/rubylouvre/archive/2011/08/28/2112321.html
前些天写html生成器的时候用到了erb,在生成html的时候是这么一句:
html=tpl.result(binding)
binding这个变量(Kernel的一个方法 T_T)有点古怪,就搜了下。它表示了ruby的当前作用域,没有任何对外可见的成员函数,唯一的用途就是传递给eval作第二个参数。因而可以这样:

def test_binding    magic='brother Chun is PURE MAN'    return bindingendeval "puts magic", test_binding

这样就穿越了一个作用域。
有时可以见到这样的构造函数:
a=Baby.new {    name "Makr"    father "Mike"    age 0.2}a.cry

好处就是好看。实现起来其实也很容易,用instance_eval:
class Baby    def initialize(&blc)        instance_eval(&blc) #here    end      def name(str=nil)        @name=str if str        @name    end    def age(num=nil)        @age=num if num        @age    end    def father(str=nil)        @father=str if str        @father    end    def cry        puts "#{name} is only #{age.to_s} year old, he wanna milk! Brother Chun is PURE MAN!"    endend#有重复代码?用class_eval缩短之,有点像宏了:class Baby    def initialize(&blc)        instance_eval(&blc)    end      def Baby.my_attr(*names)        names.each{|n|            class_eval %{                def #{n}(x=nil)                    @#{n}=x if x                    @#{n}                end            }        }    end      my_attr :name, :father, :age      def cry        puts "#{name} is only #{age.to_s} year old, he wanna milk! Brother Chun is PURE MAN!"    endend  a=Baby.new {    name "Makr"    father "Mike"    age 0.2}a.cry

这里class_eval穿越到了类的作用域,实现了动态添加函数。instance_eval也是,穿越到了实例的作用域,实现修改其内部数据。明白了它们的穿越关系,我们可以实现自己的class_eval和instance_eval——从合适的地方搞到binding就行了。
class Baby    def my_instance_eval(code)        eval code, binding    end    def Baby.my_class_eval(code='')        eval code, binding    endend#就这么简单。调用的时候就像这样:class Baby    def initialize(code)        my_instance_eval(code)    end    my_attr :name, :father, :ageenda=Baby.new %{    name "Test"    father "Orz"    age 0.2}

刚才省略了一点,那就是class_eval和instance_eval可以接受block代替字符串。搜了下,貌似没找到eval接受block的方法,所以这顶多算是只能eval字符串的山寨class_eval。
update: 想起来ruby中lambda和proc在作用域上的小区别,也就是binding的不同了。proc直接使用原先的binding,lambda继承原先作用域创建一个新的binding。

def add_method(c, m, &b)c.instance_eval { #在此使用class_eval是一样的效果define_method(m, &b)}endadd_method(String, :greet) { "Hello, " + self }p "world".greet # => "Hello, world"

热点排行