我有一个存储为字符串的“公式”数据库。让我们假设为了简单起见,每个公式包含2个由a和b表示的变量,并且公式都是良好的,并确保它只包含集合()ab+-*
中的字符。
在运行时,从此数据库中提取公式,并从另一个源获取a和b的数值,并计算公式。评估可以这样编程:
# This is how it works right now
formula = fetch_formula(....)
a = fetch_left_arg(....)
b = fetch_right_arg(....)
result = eval(formula)
这种设计有效,但我对此并不满意。它要求我的程序将自由变量命名为与公式中命名的完全相同,这很难看。
如果我的“公式”不是字符串,而是Proc对象或接受两个参数的Lambda,我可以做类似的事情
# No explicitly named variables
result = fetch_proc(...).call(fetch_left_arg(....),fetch_right_arg(....))
但不幸的是,公式必须是字符串。
我尝试以下列方式进行实验:如果从数据库中获取公式的方法将字符串包装成某个行为类似于块的内容,并且我可以将参数传递给它,该怎么办?
# This does not work of course, but maybe you get the idea:
block_string = "|a,b| #{fetch_formula(....)}"
当然我无法评估这样的 block_string ,但有哪些类似我可以使用的东西?我知道instance_eval
可以传递参数,但我应该将它应用于哪个对象?所以这也许不是一个选择....
答案 0 :(得分:3)
如果我正确地提出了你的问题,这是我最近做过的事情,并且相当容易。给出一个字符串:
s = "{|x, y| x + y}"
您可以通过执行以下操作来创建过程:
eval("Proc.new#{s}")
答案 1 :(得分:2)
这是一种非常讨厌的方法,但对于简单的公式,你提到它应该有效:
▶ formula = 'a + b'
▶ vars = formula.scan(/[a-z]+/).uniq.join(',') # getting vars names
#⇒ "a,b"
▶ pr = eval("proc { |#{vars}| #{formula} }") # preparing proc
▶ pr.call 3, 5
#⇒ 8
这里我们依赖的事实是,参数以与公式中出现的顺序相同的顺序传递给proc
。
答案 2 :(得分:1)
您可以从字符串创建lambda,如下所示:
formula = "a + b"
lambda_template = "->(a,b) { %s }"
formula_lambda = eval(lambda_template % formula)
p formula_lambda.call(1,2)
#=> 3
答案 3 :(得分:1)
避免在本地范围内创建变量的一种方法是使用Binding
:
bind = binding
formula = fetch_formula(....)
bind.local_variable_set :a, fetch_left_arg(....)
bind.local_variable_set :b, fetch_right_arg(....)
result = bind.eval(formula)
变量a
和b
现在只存在于绑定中,并且不会污染代码的其余部分。