zoukankan      html  css  js  c++  java
  • eval, class_eval, instance_eval和binding

    前些天写html生成器的时候用到了erb,在生成html的时候是这么一句:

    
       html=tpl.result(binding)
    

    binding这个变量(Kernel的一个方法 T_T)有点古怪,就搜了下。它表示了ruby的当前作用域,没有任何对外可见的成员函数,唯一的用途就是传递给eval作第二个参数。因而可以这样:

    def test_binding
        magic='brother Chun is PURE MAN'
        return binding
    end
    eval "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!"
        end
    end
    

    有重复代码?用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!"
        end
    end
     
    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
        end
    end
    

    就这么简单。调用的时候就像这样:

    class Baby
        def initialize(code)
            my_instance_eval(code)
        end
        my_attr :name, :father, :age
    end
    a=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。

  • 相关阅读:
    小球与盒子的故事
    2020.1.11 考试总结
    P4249 [WC2007]剪刀石头布
    P3825 [NOI2017]游戏
    BZOJ 2238 Mst
    P4240 毒瘤之神的考验
    生成函数(严重残缺)
    Min_25
    P3455 [POI2007]ZAP-Queries
    P3233 [HNOI2014]世界树
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2112321.html
Copyright © 2011-2022 走看看