我有一个目前看起来像这样的ruby代码:
grades.sum{|g| g.grade * g.weight}
对于用户维护的DSL,我想实现类似的东西:
grades.sum('grade' * 'weight')
这可能吗?
答案 0 :(得分:6)
不,除非你通过monkeypatch类String
添加你的应用程序逻辑(请不要这样做),否则它是不可能的。但是,如果你在instance_eval
:
grades.sum { grade * weight }
例如,我们假设grades
是一个数组:
module Enumerable
def sum(&block)
inject(0) { |acc, x| acc + x.instance_eval(&block) }
end
end
答案 1 :(得分:1)
严格地说,不,因为Ruby解释器会尝试将字符串“grade”和“weight”相乘,然后将它们作为参数传递给“sum”。你可以通过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
答案 2 :(得分:0)