您实际上是在尝试这样做:
myfunc = lambda {puts eval("word"), eval("x")}
words = ["teacher", "songwriter", "producer"]
words.each do |word|
x = 10
myfunc.call
end
但是 word 和 x 是 each() 块的局部变量,所以 lambda 块看不到它们。块可以看到它们的封闭范围——但它们看不到包含在它们封闭范围内的其他范围内。这有效:
myfunc = lambda {puts eval("word"), eval("x")}
word = "hello"
x = 10
myfunc.call
--output:--
hello
10
这是一个更有趣的版本:
func1 = lambda {puts eval "x"}
func2 = lambda {puts x}
x = "hello"
func1.call
func2.call
--output:--
hello
1.rb:23:in `block in <main>': undefined local variable or method `x' for main:Object (NameError)
from 1.rb:27:in `call'
from 1.rb:27:in `<main>'
看起来 eval() 的绑定比第二个块的闭包“更大”: eval() 的绑定是块外的整个封闭范围;但是由第二个块形成的闭包只关闭在创建块时处于封闭范围内的变量。
鉴于这个例子,这似乎是有道理的:
b = binding
myfunc = lambda {puts eval("word", b), eval("x", b)}
word = "hello"
x = 10
myfunc.call
--output:--
hello
10
所以当你写:
myfunc = lambda {puts eval("word"), eval("x")}
word = "hello"
x = 10
myfunc.call
...就好像 lambda 块正在关闭一个不可见的 b 变量, eval() 默认使用该变量,然后代码基本上是这样做的b.word = "hello"; b.x = 10
:这里的工作方式相同:
word = nil
x = nil
myfunc = lambda {puts word, x}
word = "hello"
x = 10
myfunc.call
--output:--
hello
10
换句话说,一个块关闭变量而不是值,并且块关闭的变量可以被块之后的代码更改。
有谁知道通过一个参数传递 var name 和 var value 的另一种方法?
有许多 ruby 对象可以保存不止一个数据:数组、哈希、结构、自定义类的实例等。
valid_specs = ["instrumentalist", "dj", "producer", "songwriter", "teacher"]
validate_obj = lambda do |data|
obj_name = data.var_name
obj = eval obj_name, data.binding
p "no such #{obj_name} `#{obj}`" unless valid_specs.include? obj
end
MyData = Struct.new(:var_name, :binding)
specs = ["teacher", "sweeper", "producer"]
specs.each do |spec_id|
mydata = MyData.new("spec_id", binding)
validate_obj.call(mydata)
end
--output:--
"no such spec_id `sweeper`"
但是该代码也可以像这样更简单地编写:
valid_specs = ["instrumentalist", "dj", "producer", "songwriter", "teacher"]
validate_obj = lambda do |spec_id|
p "no such spec_id `#{spec_id}`" unless valid_specs.include? spec_id
end
specs = ["teacher", "sweeper", "producer"]
specs.each do |spec_id|
validate_obj.call(spec_id)
end