我有一個目前看起來像這樣的Ruby代碼:塊像參數爲Ruby DSL
grades.sum{|g| g.grade * g.weight}
對於用戶保持DSL,我想實現這樣的:
grades.sum('grade' * 'weight')
是這可能嗎?
我有一個目前看起來像這樣的Ruby代碼:塊像參數爲Ruby DSL
grades.sum{|g| g.grade * g.weight}
對於用戶保持DSL,我想實現這樣的:
grades.sum('grade' * 'weight')
是這可能嗎?
不,這是不可能的,除非你修改類String
添加你的應用程序邏輯(請不要這樣做)。但這哪位甚至看起來beter-是可能的,如果你玩一點點與instance_eval
:
grades.sum { grade * weight }
例如,假設grades
是一個數組:
module Enumerable
def sum(&block)
inject(0) { |acc, x| acc + x.instance_eval(&block) }
end
end
嚴格地說,沒有,因爲在將它們作爲參數傳遞給「sum」之前,Ruby解釋器會嘗試將字符串「grade」和「weight」相乘。你可以通過monkeypatching String來覆蓋所有你想要允許的操作,但這是一個糟糕的想法,並且會成爲維護的噩夢。
現在,這就是說,你有幾種方法可以實現這一點。如果你需要一個完整的字符串作爲參數:
grades.sum('grade * weight')
然後,您可以使用instance_eval
到eval
代碼在你傳遞給#sum
值的情況下。實施看起來是這樣的:
class Grade
def weight
0.5
end
def grade
75
end
end
class Array
def sum(code = nil)
if block_given?
inject(0) {|s, v| s + yield(v) }
else
sum {|v| v.instance_eval code }
end
end
end
grades = Array.new(5, Grade.new)
puts grades.sum("weight * grade")
# Expected output: 5 * 75 * 0.5 = 187.5
#
# Actual output:
# % ruby grades.rb
# 187.5
這也保留原始塊的形式:
puts grades.sum {|g| g.weight * g.grade } # => 187.5
我比我的解決方案更喜歡這個。如果大括號可以接受而不是括號,這會更好地讀取IMO。 –
+1,你的答案越來越接近我需要的東西,已經解決了什麼問題,以及我到現在爲止沒有時間去調查。但我有一種印象,那就是有更多的東西......我會在搜索後回來。 –
明白了。這是[這個問題](http://stackoverflow.com/questions/10058996)和[Niklas B的這個工具](https://github.com/niklasb/ruby-dynamic-binding),完全處理這個問題。 –