深入理解 ruby 中的 eval 与 binding

来源:互联网 发布:win域名退出中国 编辑:程序博客网 时间:2024/06/06 10:01
eval(expr[, binding[, fname[, lineno=1]]])

eval 可以把字符串当做代码来执行,并返回结果;第二个参数如果是 Proc 对象或 Binding 对象,将在该对象保存的执行环境(作用域)中对字符串解析执行。

这样,就可以通过binding改变 eval 的执行上下文了!

fname,lineno,这两个参数用于发生异常时跟踪调用栈信息。

示例 1 main 环境

irb(main):001:0> var = "hello ruby"=> "hello ruby"irb(main):002:0> eval("p var")"hello ruby"=> "hello ruby"

此处待执行的字符串 “p var”的运行环境为 main;就好像是直接执行了一条 p var 语句。

当前作用域中,var 为 “hello ruby”。

示例 2 foo 环境

irb(main):006:0> def fooirb(main):007:1> var = "hello foo"irb(main):008:1> eval("p var")irb(main):009:1> end=> nilirb(main):010:0> foo"hello foo"

eval 在函数 foo 中执行,因此 eval 的执行环境也变为 foo,var 的值不再是 “hello ruby”而变为“hello foo”。

示例3 binding

binding 会生成并返回 Binding 对象,该对象包含变量、方法等环境信息,可以作为eval的第二个参数,来执行 eval 的执行环境。

代码的运行是不可能脱离环境的,如果不指定,就默认为当前环境,也就是调用 eval 的地方;如果想要灵活的指定运行环境,就需要使用binding了。

irb(main):011:0> binding=> #<Binding:0x007fd391c545b0>irb(main):012:0> binding=> #<Binding:0x007fd391c492a0>irb(main):013:0> binding=> #<Binding:0x007fd391c3c438>

当调用 binding 时,会返回一个 Binding 对象,每次返回的对象都不同,这个一定要注意。如果想要保存执行环境,多次运行eval的话,一定要使用同一个 Binding 对象。

irb(main):014:0> def bindirb(main):015:1> x = 1irb(main):016:1> bindingirb(main):017:1> end=> nilirb(main):018:0> p eval("x=5", bind)5=> 5irb(main):019:0> p eval("x", bind)1=> 1

上例中,每次调用 bind 函数都会返回一个赞新的 Binding 对象,因此执行环境中的 x 始终都是 1。

irb(main):020:0> fix = bind=> #<Binding:0x007fd391beb010>irb(main):021:0> p eval("x = 5", fix)5=> 5irb(main):022:0> p eval("x", fix)5=> 5

这次,将 bind 返回的 Binding 对象保存,执行的 eval 语句会处在相同的执行环境中,变更的内容会被保存。

实例4 binding Proc

def test_bind(x,y,&p)p “x:#{x},y:#{y}”p p.call x,yreturn bindingendp1 = Proc.new(){|x,y| x+y}p2 = Proc.new(){|x,y| x*y}b1 = test_bind(3,5,&p1)=>x:3,y:5=>8=><Binding:0xxxxxxxxx…>b2 = test_bind(3,5,&p2)=>x:3,y:5=>15=><Binding:-xxxxxxxxx>eval(‘p.call x,y’,b1)=>8eval(‘p.call x,y’,b2)=>15

Binding不仅可以保存环境作用域中的变量和self,还能保存作用域中的 block 对象。

实例5 fname, lineno

irb(main):002:0> def foo irb(main):003:1> x = 1irb(main):004:1> bindingirb(main):005:1> end=> nilirb(main):006:0> eval("1/0", foo)ZeroDivisionError: divided by 0    from (irb):4:in `/'    from (irb):4:in `foo'    from (irb):6:in `eval'    from (irb):6    from /opt/ruby200/bin/irb:12:in `<main>'irb(main):007:0> eval("1/0", foo, "dxxxx", 5)ZeroDivisionError: divided by 0    from dxxxx:5:in `/'    from dxxxx:5:in `foo'    from (irb):7:in `eval'    from (irb):7    from /opt/ruby200/bin/irb:12:in `<main>'

上例中,执行“1/0”会抛出异常,由于 irb 本来就是通过 eval 机制来运行代码的,所以会抛出两个类似的栈信息。

当不指定 fname 和 lineno 时,fname = irb, lineno 就等于binding在irb中的行数!

当指定了特定值时,栈信息会根据参数输出对应的 fname 和 lineno!

原创粉丝点击