符号和变量

symbols_2.rb

要了解符号(symbol)和标识符(例如变量名称)之间的关系,请查看我们的 symbols_2.rb 程序。首先将值 1 赋给局部变量 x。然后将符号 :x 赋给局部变量 xsymbol

  1. x = 1
  2. xsymbol = :x

此时,变量 x 和符号 :x 之间没有明显的联系。我声明了一个方法,它只需要一些传入参数并使用 p 方法查看(inspects)和显示它。我可以使用变量和符号调用此方法:

  1. # Test 1
  2. amethod( x )
  3. amethod( :x )

这是该方法打印的数据结果:

  1. 1
  2. :x

换句话说,x 变量的值是 1,因为那是分配给它的值,而 :x 的值是 :x。但是出现了有趣的问题:如果 :x 的值是 :x 并且这也是变量 x 的符号名称,是否可以使用符号 :x 来查找变量 x 的值?困惑?希望下一行代码能这些更清楚:

  1. # Test 2
  2. amethod( eval(:x.id2name))

这里,id2name 是 Symbol 类的一个方法。它返回与符号对应的名称或字符串(to_s 方法将执行相同的功能);最终结果是,当给出符号 :x 作为参数时,id2name 返回字符串 “x”。Ruby 的 eval 方法(在 Kernel 类中定义)能够计算字符串中的表达式。在本例中,这意味着它找到字符串 “x” 并尝试将其作为可执行代码进行计算。它发现 x 是变量的名称,并且 x 的值是 1。所以值 1 传递给 amethod。你可以通过运行 symbols2.rb 和比较代码的输出结果来验证这一点。

在第 20 章中更详细地解释了有关将数据作为代码来计算执行。

事情变得更加诡异。请记住,变量 xsymbol 已被赋予符号 :x

  1. x = 1
  2. xsymbol = :x

这意味着如果我们 eval :xsymbol,我们可以获得分配给它的名称 - 即符号 :x。获得 :x 后我们可以继续计算它,给出 x 的值 - 即 1:

  1. # Test 3
  2. amethod( xsymbol ) #=> :x
  3. amethod( :xsymbol ) #=> :xsymbol
  4. amethod( eval(:xsymbol.id2name)) #=> :x
  5. amethod( eval( ( eval(:xsymbol.id2name)).id2name ) ) #=> 1

正如我们所见,当用于创建属性访问器(attribute accessors)时,符号可以引用方法名称。我们可以利用它将方法名称作为符号传递给 method 方法(是的,确实存在一个名为'method' 的方法),然后使用 call 方法调用指定的方法:

  1. #Test 4
  2. method(:amethod).call("")

call 方法允许我们传递参数,为了方便,我们可以通过计算符号来传递一个参数:

  1. method(:amethod).call(eval(:x.id2name))

如果这看起来很复杂,请看一下 symbols_3.rb 中的一个更简单的示例。这从以下赋值开始:

symbols_3.rb
  1. def mymethod( somearg )
  2. print( "I say: " << somearg )
  3. end
  4. this_is_a_method_name = method(:mymethod)

这里 method(mymethod) 查找一个方法,该方法的名称由作为参数传递的符号(:mymethod)指定,如果找到,则返回具有相应名称的 Method 对象。在我的代码中,我有一个名为 mymethod 的方法,现在将其分配给变量 this_is_a_method_name

运行此程序时,你将看到第一行输出打印了变量的值:

  1. puts( this_is_a_method_name ) #=> This displays: #<Method: Object#mymethod>

这表明变量 this_is_a_method_name 已被赋予了方法 mymethod,该方法绑定到 Object 类(所有方法都作为’独立’(freestanding)函数输入)。要仔细检查变量是否真的是 Method 类的一个实例,下一行代码会打印出它的类:

  1. puts( "#{this_is_a_method_name.class}" ) #=> This displays: Method

好吧,如果它真的是一个真正的方法,那么我们应该可以调用它,不是吗?为此,我们需要使用 call 方法。这就是最后一行代码的作用:

  1. this_is_a_method_name.call( "hello world" ) #=> This displays: I say: hello world