这是验证列表和lambda,它验证对象:
valid_specs = ["instrumentalist", "dj", "producer", "songwriter", "teacher"]
validate_obj = lambda do |obj_name, valid|
obj = eval obj_name
p "no such #{obj_name} `#{obj}`" unless valid.include? obj
end
将local var传递给lambda是可以的:
spec_id = "tacher"
validate_obj.call("spec_id", valid_specs) #=> no such spec_id `tacher`
但是传递block var会导致错误:
specs = ["teacher", "songwriter", "producer"]
specs.each do |spec_id|
valid_obj.call("spec_id", valid_specs)
end
#=>:in `eval': undefined local variable or method `spec_id' for main:Object (NameError)
对我来说这似乎不太明显。我想知道原因以及如何实现我的目标,即不通过两个参数"spec_id",spec_id
传递var name和var值。
答案 0 :(得分:1)
lambda没有对它的引用,因为它没有访问调用它的作用域(除非该作用域恰好与它定义的作用域相同,因为它在第一种情况,这只是一种副作用。)
授予其访问权限的唯一方法是传递调用范围的binding
并在其上调用eval
:
f = ->(name) { eval name }
'foo'.tap { |x| f.('x') } #=> #<NameError: undefined local variable or method `x' for main:Object>
f = ->(binding, name) { binding.eval name }
'foo'.tap { |x| f.(binding, 'x') } #=> "foo"
正如您所说,唯一的另一种方法是将变量名称和值显式传递为两个参数。
答案 1 :(得分:0)
您基本上是在尝试这样做:
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值的方法?
有许多ruby对象可以容纳多个数据:数组,哈希,Structs,自定义类的实例等。
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