深入理解 ruby 中的 eval 与 binding

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 foo
irb(main):007:1> var = "hello foo"
irb(main):008:1> eval("p var")
irb(main):009:1> end
=> nil
irb(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 bind
irb(main):015:1> x = 1
irb(main):016:1> binding
irb(main):017:1> end
=> nil
irb(main):018:0> p eval("x=5", bind)
5
=> 5
irb(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
=> 5
irb(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,y
return binding
end
p1 = 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)
=>8
eval(‘p.call x,y’,b2)
=>15

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

实例5 fname, lineno

irb(main):002:0> def foo 
irb(main):003:1> x = 1
irb(main):004:1> binding
irb(main):005:1> end
=> nil
irb(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!

猜你喜欢

转载自blog.csdn.net/antony1776/article/details/77854804
今日推荐