1

这是验证对象的验证列表和 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

将本地 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)

这对我来说似乎并不明显。我想知道这样做的原因以及如何实现我的目标,即不通过两个 params 传递 var name 和 var value "spec_id",spec_id

4

2 回答 2

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"

正如您所说,唯一的另一种方法是将变量名称和值作为两个参数显式传递。

于 2013-07-28T03:37:18.113 回答
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 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
于 2013-07-28T08:17:02.573 回答