2013-07-28 21 views
1

這是驗證列表和λ,驗證對象:塊瓦爾與本地瓦爾可見度。如何通過一個參數傳遞var名稱和值給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 

傳遞局部變量,以拉姆達是OK:

spec_id = "tacher" 
validate_obj.call("spec_id", valid_specs) #=> no such spec_id `tacher` 

但經過塊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名稱和var值。

回答

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" 

唯一的另一種方法是,如你所說,要明確地傳遞兩個變量名並作爲兩個參數值。

+0

也許不是那麼美麗,但完全工作的解決方案,我一直在尋找。我也採用了簡短的語法(' - >'='lambda'和'.' ='.call')。有誰知道另一種通過一個參數傳遞var name和var value的方法嗎? –

+0

@RubyLover除了「作弊」並且在一個對象中包含兩個對象(例如傳遞一個包含兩個對象的數組)之外,還有* no *方法只用一個參數來完成。 –

0

你基本上是試圖做到這一點:

myfunc = lambda {puts eval("word"), eval("x")} 

words = ["teacher", "songwriter", "producer"] 

words.each do |word| 
    x = 10 
    myfunc.call 
end 

但Word和x的每()塊的局部變量,因此拉姆達塊不能看到它們。塊可以看到他們的封閉範圍 - 但他們不能看到其封閉範圍內包含的其他範圍內。這工作:

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 

換句話說,一個塊關閉了變數 - 沒有價值觀,一個塊關閉了可通過該塊後到來代碼被改變的變量。

Does anyone know another ways to pass var name and var value via one param?

有可容納多於一個的數據塊許多紅寶石對象:陣列,散列,結構,自定義類的實例,等

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