符号与字符串

一个常见的误解就是认为符号(symbol)是字符串的一种类型。毕竟,符号 :hello 与字符串 “hello” 非常相似不是吗?

事实上,符号与字符串完全不同。首先,每个字符串是不同的 — 因此,”hello”、”hello” 和 “hello” 是三个独立的对象,具有三个独立的 object_ids。

symbol_ids.rb
  1. puts( "hello".object_id ) # These 3 strings have 3 different object_ids
  2. puts( "hello".object_id )
  3. puts( "hello".object_id )

但是符号是唯一的,所以 :hello:hello:hello 都引用具有相同的 object_id 的对象。在这方面,符号与整数(integer)相比,要比字符串有更多的共同之处。你可能还记得,给定的整数值每次出现都引用相同的对象,因此 101010 可以被认为是相同的对象,并且它们具有相同的 object_id

ints_and_symbols.rb
  1. # These three symbols have the same object_id
  2. puts( :ten.object_id )
  3. puts( :ten.object_id )
  4. puts( :ten.object_id )
  5. # These three integers have the same object_id
  6. puts( 10.object_id )
  7. puts( 10.object_id )
  8. puts( 10.object_id )

或者你可以使用 equal? 方法测试其相等性:

symbols_strings.rb
  1. puts( :helloworld.equal?( :helloworld ) ) #=> true
  2. puts( "helloworld".equal?( "helloworld" ) ) #=> false
  3. puts( 1.equal?( 1 ) ) #=> true

由于是唯一的,所以符号提供了明确的标识符。你可以将符号作为参数传递给方法,如下所示:

  1. amethod( :deletefiles )

方法可能包含测试传入参数的值的代码:

symbols_1.rb
  1. def amethod( doThis )
  2. if (doThis == :deletefiles) then
  3. puts( 'Now deleting files...')
  4. elsif (doThis == :formatdisk) then
  5. puts( 'Now formatting disk...')
  6. else
  7. puts( "Sorry, command not understood." )
  8. end
  9. end

符号还可用于提供字符串的可读性和整数的唯一性的 case 语句:

  1. case doThis
  2. when :deletefiles : puts( 'Now deleting files...')
  3. when :formatdisk : puts( 'Now formatting disk...')
  4. else puts( "Sorry, command not understood." )
  5. end

声明符号的作用域不会影响其唯一性。思考以下…

symbol_ref.rb
  1. module One
  2. class Fred
  3. end
  4. $f1 = :Fred
  5. end
  6. module Two
  7. Fred = 1
  8. $f2 = :Fred
  9. end
  10. def Fred()
  11. end
  12. $f3 = :Fred

这里,变量 $f1$f2$f3 在三个不同的作用域内分配了符号 :Fred:模块 One,模块 Two 和 ‘main’ 作用域。我将在第 12 章中对模块(modules)进行更多说明。现在,只需将它们视为定义不同作用域的“命名空间”(namespaces)即可。然而每个变量引用着相同的符号 :Fred,并且具有相同的 object_id

  1. # All three display the same id!
  2. puts( $f1.object_id )
  3. puts( $f2.object_id )
  4. puts( $f3.object_id )

即便如此,符号的“含义”(meaning)也会根据其作用域而变化。

换句话说,在模块 One 中,:Fred 引用类 Fred,在模块 Two 中,它引用常量 Fred = 1,在 main 作用域内引用 Fred 方法。

上一个程序的重写版本证实了这一点:

symbol_ref2.rb
  1. module One
  2. class Fred
  3. end
  4. $f1 = :Fred
  5. def self.evalFred( aSymbol )
  6. puts( eval( aSymbol.id2name ) )
  7. end
  8. end
  9. module Two
  10. Fred = 1
  11. $f2 = :Fred
  12. def self.evalFred( aSymbol )
  13. puts( eval( aSymbol.id2name ) )
  14. end
  15. end
  16. def Fred()
  17. puts( "hello from the Fred method" )
  18. end
  19. $f3 = :Fred
  20. One::evalFred( $f1 ) #=> displays the module::class name: One::Fred
  21. Two::evalFred( $f2 ) #=> displays the Fred constant value: 1
  22. method($f3).call #=> calls Fred method: displays: "hello from the Fred method"

当然,由于变量 $f1$f2$f3 引用着相同的符号,因此你使用的变量是在任意地方指定的都是无关紧要的。以下产生完全相同的结果:

  1. One::evalFred( $f3 )
  2. Two::evalFred( $f1 )
  3. method($f2).call