Ruby 中的 Symbol 类

yufei       6 年, 1 月 前       1604

其实我一直对 symbol 很感兴趣,但是一直没有理解 symbol 到底有啥用,怎么用,我自己也想借这篇文章来了解下。

对了,symbol 我们一般翻译为 「 符号 」,很通俗易懂。

我觉得如果要理解 symbol ,就要弄清楚下面三个知识点:

  • symbol 是什么意思?
  • Ruby 中的 symbol 到底是什么?
  • Ruby 中的 Symbol 类
  • Ruby 中的 Symbol 类背后的逻辑

symbol 是什么意思?

symbol 一般用于标识可以量化的资源。如果用普通人能理解的方式,就是创建一个可以量化的单位。比如 , 可以一个人,两个人,三个人,可以一个豆子,两个豆子。比如长度单位 cm (厘米),比如 1cm 、10cm、100cm。

symbol 必须是唯一的,这样大家看到的这个 symbol 的时候都是一个意思,比如,你不能在北京说一个栗子是 1 栗子 ,在天津说一个栗子是 2 栗子

Ruby 中的 symbol 到底是什么?

Ruby 中的 symbol 是 Symbol 类的实例,用于唯一标识特定资源。这个资源可以是:

  • 一个方法
  • 一个变量
  • 一个哈希键 ( key )
  • 一个状态 ( state )
  • 等等

symbol 必须是唯一的,因为运行中的程序,对于特定的资源,只能创建一个唯一的 Symbol 类的实例。

比如下面的代码,输出的都是唯一的值

demo.rb

p :pending.object_id
p :pending.object_id

运行结果如下

[root@www.twle.cn ruby]# ruby demo.rb
989468
989468

从上面的输出中可以看到,:pending 符号虽然调用了两次,但实际上只创建一次,因为 :pending.object_id 返回相同的对象标识符。

symbol 与字符串的区别

symbol 常常会与字符串比较,也会被常常问到: symbol 与字符串有什么区别

symbol 类似于字符串,但与字符串有着本质的区别:

对于每一个新的字符串,都会创建一个新的 String 类的实例。但对于 symbol ,永远只会创建一次 Symbol 类的实例

例如下面的代码

demo.py

p 'pending'.object_id
p 'pending'.object_id

运行结果如下

[root@www.twle.cn ruby]# ruby demo.rb
70119665954000
70119665953860

经过上面的学习,我们已经对 symbol 有了基本的了解,现在,我们来看看 Symbol 类及它提供的方法

Ruby 中的 Symbol 类

Symbol 类是 Ruby 核心库 ( core-lib ) 的一部分。

虽然 Symbol 是一个类,但它没有提供公开的构造方法 ( Symbol.new ),也就不能公开实例化。例如下面的代码会报错

[root@www.twle.cn ruby]# irb
irb(main):001:0> Symbol.new
NoMethodError: undefined method `new' for Symbol:Class
    from (irb):1
    from /usr/bin/irb:11:in `<main>'
irb(main):002:0> 

但是,我们可以使用冒号 : 加上 symbol 的标识来隐式的创建一个 Symbol 的实例。就像上面我们创建 :pending 一样

irb(main):002:0> :pending.class
=> Symbol

我们可以调用 Symbol.ancestors 来看看 Symbol 的祖先链,也就是 Symbol 的继承链

irb(main):003:0> Symbol.ancestors
=> [Symbol, Comparable, Object, Kernel, BasicObject]

从祖先链中可以看到,Symbol 类默认继承自 Object 类。

另外需要注意的是,Symbol 包含了 Comparable 模块,也就可以用于比较两个 symbol 是否相等

irb(main):004:0> :sm == :cm
=> false
irb(main):005:0> :sm > :cm
=> true
irb(main):006:0> :sm < :cm
=> false
irb(main):007:0> :sm == :sm
=> true

如果你稍加留意,就会发现 Symbol 类与 String 类和 Numberic 类有着同样的祖先链

irb(main):008:0> String.ancestors
=> [String, Comparable, Object, Kernel, BasicObject]
irb(main):011:0> Numeric.ancestors
=> [Numeric, Comparable, Object, Kernel, BasicObject]

Symbol 类提供了许多有趣的方法,这些方法大致可以分为三大类:比较、修改和匹配。大多数修改和匹配符号的方法都使用 Symbol#to_s 方法转换为字符串,以便使用符号的 String 表示

Ruby 中的 Symbol 类背后的逻辑

我们在上面有说过,每一个 symbol 都是唯一的,都是 Symbol 类的唯一实例。因此,Ruby 必须跟踪它们中的每一个以确保它们的唯一性。

为了实现唯一和跟踪目的,Ruby 提供了一个名为 global_symbols 的内部表,负责跟踪正在运行的程序的所有符号。

注意:对于所有 Ruby 版本< 2.2.0 来说,符号只会被放入内存一次。这使得它们使用起来非常有效。它们会一直留在内存中直到程序退出。但比这个更高的版本,符号也会被垃圾收集。

因为对于 Ruby > 2.2.0 的版本,symbol 也会参与垃圾搜集,所以,一般情况下我们并不直接使用 global_symbols 全局表(也获取不到)。Ruby 提供了 Symbol.all_symbols 方法返回一个数组,该数组表示方法调用时 global_symbols 表的内容。

你可以运行下面的代码来演示和查看每次调用 Symbol.all_symbols 方法的不同

Symbol.all_symbols.length                      # => 3893
Symbol.all_symbols.grep(/Struct/)              # => [:Struct]

:dummy_symbol
Symbol.all_symbols.length                      # => 3894
Symbol.all_symbols.grep(/dummy_symbol/)        # => [:dummy_symbol]

dummy_variable = nil
Symbol.all_symbols.length                      # => 3895
Symbol.all_symbols.grep(/dummy_variable/)      # => [:dummy_variable]

def dummy_method; end
Symbol.all_symbols.length                      # => 3896
Symbol.all_symbols.grep(/dummy_method/)        # => [:dummy_method]

class DummyClass; end
Symbol.all_symbols.length                      # => 3897
Symbol.all_symbols.grep(/DummyClass/)          # => [:DummyClass]

Symbol.all_symbols.grep(/Hash/)                # => [:Hash]
class Hash; end
Symbol.all_symbols.length                      # => 3897

第一个 Symbol.all_symbols.length 输出结果可能因为版本的不同而不同。

在文章开篇时我们就说过,一个符号可以是一个变量,或一个方法,或一个类。

从输出结果中可以看到,程序刚开始时,global_symbols 并不是空的,而是存在 3800+ 个符号,这是为什么呢?

这是因为,对于 Ruby 程序来说,该表填充了 Ruby 核心库中包含的所有方法,变量和类。这些资源作为符号插入表中。

例如,Struct 类就是 Ruby 核心库的一部分。

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.