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!