(转)理解 Ruby Symbol

Symbol 是什么

  Ruby 是一个强大的面向对象脚本语言(本文所用 Ruby 版本为1.8.6),在 Ruby 中 Symbol 表示“名字”,比如字符串的名字,标识符的名字。创建一个 Symbol 对象的方法是在名字或者字符串前面加上冒号:

创建 symbol 对象

:foo
:test 

 

:”abc
:"I am a boy”

 

  你可能会问,字符串就是字符串,干吗还有字符串的名字?这是因为在 Ruby 中字符串也是一种对象,即 String 对象。无论其结构还是操作和 Symbol 对象都是不同的。在 Ruby 中每一个对象都有唯一的对象标识符(Object Identifier),可以通过 object_id 方法来得到一个对象的标识符。我们来看看 Symbol 对象和 String 对象的差别:

Ruby 对象标识符

irb(main):001:0> puts :foo.object_id
327458
=> nil
irb(main):002:0> puts :foo.object_id
327458
=> nil
irb(main):003:0> puts :"foo".object_id
327458
=> nil
irb(main):004:0> puts "foo".object_id
24303850
=> nil
irb(main):005:0> puts "foo".object_id
24300010
=> nil
irb(main):006:0> puts "foo".object_id
24296170
=> nil

 

  可以看到,前三行语句中的 :foo (或者 :"foo")都是同一个 Symbol 对象,其 object id 为327458,而后三行中的字符串”foo”都是不同的对象,其 object id 依次为24303850、24300010、24296170。

  可见,每个 String 对象都是不同的,即便他们包含了相同的字符串内容;而对于 Symbol 对象,一个名字(字符串内容)唯一确定一个 Symbol 对象。

值得注意的是创建 Symbol 对象的字符串中不能含有’\0’字符,而 String 对象是可以的。

非法 Symbol 字符串

irb(main):001:0>  :"fo\0o"
SyntaxError: compile error
(irb):1: symbol cannot contain '\0'
        from (irb):1
irb(main):002:0> :"foo\0"
SyntaxError: compile error
(irb):2: symbol cannot contain '\0'
        from (irb):2
irb(main):003:0> puts "foo\0".object_id
24305140
=> nil
irb(main):004:0> puts "fo\0o".object_id
24301000
=> nil
irb(main):005:0>

 

  除了可以采用一般的字符串,还可以使用操作符(例如+, -, *, /),变量,常量,方法甚至类的名字来创建 Symbol 对象,例如:+就是一个合法的 Symbol 。实际上,在 Ruby 内部操作符、变量等名字本身就是作为 Symbol 处理的,例如当你定义一个实例变量时, Ruby 会自动创建一个 Symbol 对象,例如 @test 对应为 :@test 。

实例变量的 Symbol

class Test
 attr_accessor :test
end

 

  这个类定义了一个具有读写方法的实例变量 @test 。实际上 Ruby 创建了两个 Symbol ,一个是实例变量的 symbol :@test ,另一个是 :test 。那如果使用字符串对象 ”test” 作为参数呢?也可以,仍然会创建两个 symbol ,:test 和 :@test ,为什么还会创建 :test 呢?这是和Ruby的实现相关的(至少Ruby1.8.6里是这样)。

注意,类变量 @@test 和实例变量 @test 对应的 Symbol 显然是不同的。记住:名字相同,则Symbol 相同

名字相同, Symbol 相同

class Test
  puts :Test.object_id
  Test = 10
  puts :Test.object_id

  def Test
    puts :Test.object_id
  end
end 

Test.new.Test

运行结果

224298
224298
224298

 

名字不同, Symbol 不同

class Test
  puts :Test.object_id

  @@test = 10
  puts :@@test.object_id
  def test
    puts :test.object_id
    @test = 10
    puts :@test.object_id

  end
end 

t =Test.new
t.test  

运行结果

224298
288068
79858
288108

 

  第一个例子里,类名、常量名和方法名都是 Test ,因此相应的 Symbol 对象都是 :Test 。不用担心, Ruby 可以很好区分它在不同上下文中到底表示什么。当然这并不是一个好的编程风格,但对于理解 Ruby 的 Symbol 还是有帮助的: Symbol 表示一个名字,仅此而已。

  Symbol 对象一旦定义将一直存在,直到程序执行退出。所有 Symbol 对象存放在 Ruby 内部的符号表中,可以通过类方法 Symbol.all_symbols 得到当前 Ruby 程序中定义的所有 Symbol 对象,该方法返回一个 Symbol 对象数组。由于 Symbol 比较多,你可以 dump 到文件中来查看。

all_symbols 方法

irb(main):001:0> Symbol.all_symbols.size
=> 4047
irb(main):002:0> Symbol.all_symbols[0..9]
=> [:@level_notifier, :ppx, :msg_dn, :version, :secs, :@user, :pos, :socketpair,
 :TkENSURE, :HTTPAccepted]
irb(main):003:0> File.open("sym", "w") do |file| file.puts Symbol.all_symbols end
=> nil

 

Symbol 和 String

  Symbol 对象和 String 对象是完全不同的东西,对象标识符很明确的说明了这一点。除此之外,我们还可以从两种对象的方法上区分。查看 Ruby 库参考,你会发现 String 类有非常多的方法,包括 Mixed-in 方法(Ruby中一个类通过 include 其他模块而得到的方法,实现多重继承的效果)、类方法和实例方法;而 Symbol 类只有一个类方法 all_symbols 和7个实例方法。例如,可以通过 []= 方法改变 string 的内容,而 symbol 则不行:

[]= 方法比较

irb(main):001:0> s="test"
=> "test"
irb(main):002:0> s[0]='1'
=> "1"
irb(main):003:0> puts s
1est
=> nil
irb(main):004:0> sym=:test
=> :test
irb(main):005:0> sym[0]=1
NoMethodError: undefined method `[]=' for :test:Symbol
        from (irb):5
irb(main):006:0>

 

  虽然 Symbol 和 String 是不同的对象,但它们之间关系很密切。 Ruby 提供了方法在 Symbol和 String 之间转换。Symbol 转化为 String,使用 to_s 或 id2name 方法将 Symbol 转化为一个 String 对象:

Symbol 到 String

irb(main):001:0> :test.id2name
=> "test"
irb(main):002:0> :test.to_s
=> "test"
irb(main):003:0> :"I am a boy".to_s
=> "I am a boy"

  

  注意,每个 String 对象都是唯一的,因此对一个 Symbol 调用多次将产生多个 String 对象。

  String 转化为 Symbol,除了在字符串前面加冒号,还可以使用 to_sym 或 intern 方法将 String 转化为 Symbol ,如果该 Symbol 已经存在,则直接返回。

String 到 Symbol

irb(main):001:0> var1 = "test".to_sym
=> :test
irb(main):002:0> var2 = "test".intern
=> :test
irb(main):003:0> var1 == var2
=> true
irb(main):004:0>

 

使用 Symbol

  正如前边提到的, Ruby 内部一直在使用 Symbol ,比如 Ruby 程序中的各种名字,Symbol本质上是 Ruby 符号表中的东西。使用 Symbol 处理名字可以降低 Ruby 内存消耗,提高执行速度,这点我们在下一篇文章中会看到。

  那么 Symbol 对我们有什么用呢?当然也是内存。使用 String 的开销太大了,因为每一个String 都是一个对象。想想前边的例子,一个字符串每出现一次 Ruby 就会创建一个 String 对象。

  通常来讲,当你面临 String 还是 Symbol 的选择时,可以参考以下标准:

  • 如果使用字符串的内容,这个内容可能会变化,使用 String
  • 如果使用固定的名字或者说是标识符,使用 Symbol

  那么什么时候我们会用到名字呢?很多时候都会,比如枚举值、关键字(哈希表关键字、方法的参数)等等;作为哈希表的 key,哈希表是 Symbol 应用最为广泛的地方。

在ruby中,哈希和数组类似,一个哈希表是一系列 key/value 对的集合,只不过它的 key 取值范围更广泛,可以是任何对象,比如正则表达式。但通常我们都会取有意义的 key ,比如 String、Symbol 。

下面这个哈希表表示按城市分类的一些机器的集合。

一个哈希表例子

hosts{
       'beijing' => 'machine1',
       'shanghai'  => 'machine2',
       'guangzhou' => 'machine3',
       'tianjin' =>  'machine4',
       'shenzhen' => 'machine5'
}

  

  如果要引用 beijing 的机器,使用 hosts['beijing'] 。但如果我们程序中要频繁引用哈希表中 value ,这样就不大好了,因为 Ruby 对每一次字符串引用都会生成一个 String 对象,累积下来这个开销是相当大的。

我们完全可以使用 Symbol ,因为对于这些 key 来讲,我们用的就是名字而已,例如下面hosts[:beijing]

 

使用 Symbol 作为 key

hosts = {
 :beijing => 'machine1',
 :shanghai => 'machine2',
 :guangzhou => 'machine3',
 :tianjin  => 'machine4',
 :shenzhen => 'machine5'
}

 

哈希参数

  通常我们定义的函数的参数的个数和顺序是写死的,调用函数的时候要确保参数的个数、顺序匹配,有时候这样很不方便,使用哈希参数可以解决这个问题。ROR 中就大量地运用这种方式,也许你已经看到了,到处都是 Symbol 和哈希。比如:

使用哈希参数的方法调用

link_to 'Show', :action => 'show', :id => product

add_column :products, :price, :decimal,
:precision => 8, :scale => 2, :default => 0

使用哈希参数的方法可以如下定义,前半部分为固定参数,后面为可变参数,或者干脆全采用哈希参数:

哈希参数

def my_method(para1, …, options={})
#your code
end

def my_method(options={})
#your code
end

   

  如果你希望设定一些默认参数,并允许调用者更改这些参数,可以使用哈希对象的 merge! 方法, hsh.merge!( other_hash )该方法将 other_hash 里内容加到 hsh 中,如果other_hash hsh 有重复的 key ,则 key other_hash 中的 value 覆盖 hsh 中对应 key value

方法定义-使用默认参数

class Test
 def my_method(opts={})
  default_opts={:arg1 => 10, :arg2 => "abc"}
  default_opts.merge!(opts)
  default_opts.each{|key,value| puts "#{key} is #{value}"}
 end
end

t = Test.new
t.my_method :arg1=>5, :arg3=>"def"

运行结果

arg1 is 5
arg2 is abc
arg3 is def

 

 

 

注:本文转自:http://www.ibm.com/developerworks/cn/opensource/os-cn-rubysbl/, 详细信息请访问文章出处,本文仅供学习之用。

时间: 2024-10-01 04:36:06

(转)理解 Ruby Symbol的相关文章

比较详细的ruby symbol 学习资料_ruby专题

解释的不错,应该明确了不少 ruby symbol详解 起因 最近在学习ruby on rails,的确是一个优秀的数据库开发框架.但在过程中,发现在视图文件夹中的rhtml文件里有大量的类似于以下的语句: <td><%= link_to recipe.title, :action => "show", :id => 1 %></td> 这是一个指向链接,如果没有冒号这句话的意思很好理解:这是一个指向http://127.0.0.1:30

《Effective Ruby:改善Ruby程序的48条建议》一第1条:理解Ruby中的True

第1条:理解Ruby中的True 似乎每门语言处理布尔值都有其自己的方式.有些语言仅有一种真假值的表示方法.其他语言使用令人困惑的多种类型来表示,它们时真时假.当对条件表达式的真假值判断错误时会导致程序错误.比如,你知道有多少语言用零值表示假吗?零值为真的语言又有哪些呢?Ruby有自己的做事方式,包括布尔值.幸好,区别真假值的规则非常简单.因为它不同于其他语言(这也是写这一条的原因),所以请确认你理解了以下内容.在Ruby中,除了false和nil,其他值都是真值.我们有必要花点时间来想一想这意

举例理解Ruby on Rails的页面缓存机制_ruby专题

有了页面缓存,Rails 就可以不再介入.在某种程度上,这是件好事,因为您的确可以获得优秀的性能.Rails 只需创建 HTML 页面,将其放入目录,之后,就可以置之于脑后.从那时起,就由应用服务器管理这些页面,且页面进入应用服务器无需任何循环.从性能的角度而言,页面缓存真是天赐之福. 我也钟爱页面缓存,Rails 使之简单利落.只需使用一行代码就可以启用缓存.如果再加入一些代码,就能通过简单地删除文件操作或使用 Rails 较高层的 API 终止缓存.这里存在一个问题.并不是每个网站都能使用页

深入理解Ruby中的block概念_ruby专题

Ruby 里的 block一般翻译成代码块,block 刚开始看上去有点奇怪,因为很多语言里面没有这样的东西.事实上它还不错.First-class function and Higher-order function First-class function 和 Higher-order function 是函数式编程语言里面的概念,听起来好像很高端的样子,其实很很简单的. First-class functions 是指在某些语言里,函数是一等公民,可以把函数当做参数传递, 可以返回一个函数

深入理解Ruby中的代码块block特性_ruby专题

block是什么? 在Ruby中,block并不罕见.官方对block的定义是"一段被包裹着的代码".当然,我觉得这样的解释不会让你变的更明白. 对block的一种更简单的描述是"一个block就是一段存储在一个变量中的代码,它和其他的对象一样,可以被随时的运行" 然后,咱们通过看一些代码,之后再把这些代码重构成Ruby中的block形式.通过代码来实际的感受,更加直观. 比如,对两个数做加法? puts 5 + 6 # => 11 嗯,这样写是可以的.但是,

深入理解Ruby on Rails中的缓存机制_ruby专题

几个场景 首先,让我先来带您浏览几个 ChangingThePresent.org 中的页面吧.我将显示站点中几个需要缓存的地方.然后,再指出我们为其中每个地方所做出的选择以及为实现这些页面所使用的代码或策略.尤其会重点讨论如下内容:     全静态页面     几乎无变化的全动态的页面     动态页面片段     应用程序数据 先来看看静态页面.几乎每个站点都会有静态页面,如图 1 所示,其中还有我们的条款和条件.可以通过单击 register 然后再选择是否接受用户协议来浏览相应页面.对于

ruby:理解Symbol的二篇好文章

理解 Ruby Symbol,第 1 部分 http://www.ibm.com/developerworks/cn/opensource/os-cn-rubysbl/   理解 Ruby Symbol ,第 2 部分: Symbol 内幕http://www.ibm.com/developerworks/cn/opensource/os-cn-rubysbl2/index.html

《Ruby程序员修炼之道》(第2版)目录—导读

版权 Ruby程序员修炼之道(第2版) • 著 [美] David A. Black 译 钟凤鸣 陈雪静 责任编辑 杨海玲 • 人民邮电出版社出版发行 北京市丰台区成寿寺路11号 邮编 100164 电子邮件 315@ptpress.com.cn 网址 http://www.ptpress.com.cn • 读者服务热线:(010)81055410 反盗版热线:(010)81055315 版权声明 Original English language edition, entitled The W

使用Ruby程序实现web信息抓取的教程_ruby专题

网站不再单单迎合人类读者.许多站点现在支持一些 API,这些 API 使计算机程序能够获取信息.屏幕抓取 -- 将 HTML 页面解析为更容易理解的表单的省时技术 - 仍然很方便.但使用 API 简化 Web 数据提取的机会在快速增多.根据 ProgrammableWeb 的信息,在本文发表时,已存在 10,000 多个网站 API - 在过去的 15 个月中增加了 3,000 个.(ProgrammableWeb 本身提供了一个 API,可从其目录中搜索和检索 API.mashup.成员概要文