eval 魔法
eval
方法提供了一种执行字符串的 Ruby 表达式的简单方法。乍一看,eval
可能看起来与双引号字符串中的 #{}
标记限定表达式完全相同。以下两行代码产生相同的结果:
eval.rb
puts( eval("1 + 2" ) )
puts( "#{1 + 2}" )
但是,有时候结果可能不是你所期望的。请看以下内容,例如:
eval_string.rb
exp = gets().chomp()
puts( eval( exp ))
puts( "#{exp}" )
假设你输入 2 * 4
并将其分配给 exp
。当你使用 eval
计算 exp
时,结果为 8,但是当你在双引号字符串中计算 exp
时,结果为 ‘2 * 4’。这是因为 gets()
读入的任何内容都是字符串,"#{exp}"
将其作为字符串而不是表达式进行计算,而 eval(exp)
将字符串作为表达式求值。
为了强制在字符串中进行求值,你可以在字符串中放置 eval
(尽管如此,可能会偏离我们的目标):
puts( "#{eval(exp)}" )
这是另一个例子。尝试一下,并在出现提示时按照说明操作:
eval2.rb
print( "Enter the name of a string method (e.g. reverse or upcase): " ) # user enters: upcase
methodname = gets().chomp()
exp2 = "'Hello world'."<< methodname
puts( eval( exp2 ) ) #=> HELLO WORLD
puts( "#{exp2}" ) #=> "Hello world".upcase
puts( "#{eval(exp2)}" ) #=> HELLO WORLD
eval
方法可以执行计算跨越多行的字符串,从而可以执行嵌入字符串中的整个程序:
eval3.rb
eval( 'def aMethod( x )
return( x * 2 )
end
num = 100
puts( "This is the result of the calculation:" )
puts( aMethod( num ))' )
有了所有这些 eval
的能力,现在让我们看看编写一个它自己可以编写程序的程序是多么容易。这里:
eval4.rb
input = ""
until input == "q"
input = gets().chomp()
if input != "q" then eval( input ) end
end
这可能看起来不多,但是这个程序允许你从命令提示符中创建和执行真正可用的 Ruby 代码。试试看。运行程序并一次一行地输入这两个方法(但是不要点 ‘q’ 来退出 - 我们稍后会写一些代码):
def x(aStr); puts(aStr.upcase);end
def y(aStr); puts(aStr.reverse);end
请注意,你必须在一行中输入每个整个方法代码,因为我的程序在输入时按行执行。我将在后面解释如何解决这个限制。归功于 eval
,每个方法都变成了真实可行的 Ruby 代码。你可以通过输入以下内容来证明这一点:
x("hello world")
y("hello world")
现在,这些表达式本身已被执行,它们将调用我们刚刚编写的两个方法,从而产生以下输出:
HELLO WORLD
dlrow olleh
仅仅五行代码很不错了!