最近看了一阵的ruby on rails ,发现其的确是一个优秀的数据库开发框架。但在过程中,感觉ruby 语法的怪异是所不能接受的,尤其对Symbol中的相关只是很是不接,在rails框架中到处充斥这:action这类“符号”。
ruby面向对象特性的一个缺点
ruby中,一切皆是对象。就一个简单的字符串举例:
ruby -e 'puts "hello world".class'
String
这里打印了"hello world"的字符串所属的类,结果显示它是一个String对象的实例。我们还可以显示它的对象号。
ruby -e 'puts "hello world".object_id'
41436168
ruby一向标榜自己是完全的面向对象的原因就在于此,它的确做的很彻底。但是凡事有好就有坏,一个对象占用的内存空间显然会比纯粹的变量大得多,当程序中涉及到大量的字符串时,一个ruby程序会占用过多的内存。举个例子说:
我们用hash列表来存储歌曲的信息
song1 = { 'title' => 'used to love you', 'artist' => 'john legend'}
song2 = { 'title' => 'i still', 'artist' => 'backstreet boys'}
#......
#很多歌,这里只用两首
for i in 1..2
thesong="song"+i.to_s
eval <<-PROC
#{thesong}.each_key { |key| puts key.object_id.to_s }
PROC
end
结果:
41436144
41436408
41435904
41436000
因为object_id各不相同,在hash表中的各个key都是独立的String对象,即使内容相同(如'title'),ruby还是将其视为不同的对象,这样就无端地占用了不少内存。但事实上,大多数情况下,我们仅将hash中的key视为字段而已,不会涉及到String类的方法,ruby自动将其设置为对象有杀鸡用牛刀之嫌。
symbol是什么
直译来说就是“符号”,在ruby就是形如:action这样的,一个冒号后跟一段字符串。显然,根据“一切都是对象”定律,它也是一个对象。
ruby -e ' puts :action.class '
Symbol
这个对象存在的意义在于,它解决了“同内容字符串,不同对象”带来的过多占用内存的问题。简单的说:action代表了'action'字符串,这里说的是字符串,不是字符串对象。
ruby -e ' puts :action '
action
更确切的讲就是一个symbol对象代表该对象的冒号后的字符串。
ruby -e ' puts :action '
action
ruby -e ' puts :"hello world" '
hello world
所有同内容的字符串只需要一个标记对象就可以代替,这样减少了不必要的对象建立和内存占用。但是,正如我强调的“symbol代表的是字符串,不是对象”,因此不要希望标记可以使用String类的诸如capitalize,center等方法,如果使用的话只会得到提示方法未定义的错误报告:
ruby -e ' puts :action.capitalize '
-e:1: undefined method 'capitalize' for :action:Symbol' (NoMethodError)
幸运的是,symbol提供了转换函数to_s用来生成一个字符串对象,它会提取字符串内容并将其升级为对象。
ruby -e ' puts :action.to_s.capitalize '
Action
另外,很重要的一点是,symbol没有赋值方法,换句话说symbol一旦定义,将不能改变。
ruby -e ' :action="hello" '
syntax error
很遗憾,即使使用了to_s,赋值依然无法顺利进行,因为ruby会认为“to_s=”是一个未定义函数。除非明确地为被转换生成的字符串对象指定一个引用(但事实上在复制之后该连接的指向又发生了变化):
:action
myaction=:action.to_s
myaction="lala"
puts myaction
结果:
lala
对Symbol的总结
1. 在一个名字或者字符串前面加上冒号,得到一个symbol对象。还可以通过String#to_sym、Fixnum#to_sym和String#intern得到。
2. 一般用symbol做hash的key,号称是为了节省内存,提高执行效率。
3. 为什么可以节省内存?Ruby中的String是可变对象,这一点跟Java、C#、Python都不一样。注意跟某些C++标准库中的COW的basic_string<t></t>也不一样。Ruby中每一个String都可以就地改变。可能是因为这个原因,Ruby中两个内容相同的字符串文本量实际上是两个不同的对象。
a = "hello"
b = "hello"
虽然俩字符串内容都一样,但是你比一下a和b,就知道a.object_id != b.object_id,它们指向的不是同一个对象。结果反而很像未经string pooling优化的C语言的行为。到底immutable好还是mutable好,或者还是貌似聪明的COW好,见仁见智了。不过Ruby的设计在把字符串用作hash key的时候毛病就大了。比如你写:
h["ruby"].name = "Ruby"
h["ruby"].author = "matz"
h["ruby"].birth_year = 1995
的时候,"ruby"这个字符串动态生成了三次,占用三倍内存。这就严重地浪费了内存。而用:ruby做为key,因为在整个运行过程中,Ruby runtime保证名为:ruby的symbol对象只有一个,所以就不用生成三个,节省内存。
4. 为什么可以提高执行效率?显然的原因是免得多次动态生成'ruby'字符串了。还不单如此,Hash的key值应该是常量,所以Ruby的Hash对于作为key的String对象都要施加保护,所谓保护,也就是把String冻结了,免得你之后还改变其值。保护当然是有代价的,symbol无需保护,当然是能提高效率的。附带说明,其他mutable的对象也可以作为hash的key,这是Ruby设计得比较奇怪的地方。在irb里运行以下代码,你会发现Ruby的Hash丢值。
h = Hash.new
L = [1, 2]
h[L] = "A big object!"
L << 3 # 居然能改!
h[L] # ==> nil,找不到了,似乎正常
# 可是
h[[1, 2]] # ==> nil,居然还是找不到
# 看看keys
h.keys # ==> {[1, 2, 3]} 似乎还在里面
h[[1, 2, 3]] # ==> nil
# 可是
h # ==> {[1, 2, 3]=>'A big object'},明明在这里,就是找不到
h.rehash # ==> 这样就会一切恢复正常。
这一点上Python的设计要比较容易理解,list根本就是unhashable的,不能用来做hash的key。
回过头来在说提高效率的事。Symbol效率提高还有第三个原因,那是因为symbol本质上不比一个整数多出多少东西,用Symbol#to_i可以得到一个在整个程序中唯一的整数。Hash完全可以利用这个整数来产生hash值,那岂不是比根据字符串内容去算hash值快得多?这还是小意思,既然这个整数是唯一的,那么产生一个唯一的hash值也就是小菜一碟,要是能保证hash值唯一,那还是什么hash表,根本就变成数组了。Hash表还可能会冲突,数组根本不会冲突,百分之百保证O(1),当然快。我没看Ruby源码,不知道是不是这么处理的。
5. 为什么Ruby runtime可以保证每一个symbol唯一?因为Ruby把symbol存放在运行时维护的一个符号表里了,而这个符号表实际上是一个atom数据结构,其中存储着当前所有的程序级的name,确保不出现内容相同的多个对象。几乎每一个语言和系统都会有这样一个符号表,只不过象C/C++那样的语言,这个符号表只是在编译时存在,运行时就没了。而Python、Ruby则在运行时也保留这张表备用。有这样一个现成的数据结构干嘛不用?
6. 但是这个表中存放的并不光是我们自己主动生成的symbols,还有Ruby解释器对当前程序进行词法分析、语法分析后存在其中的、当前程序的所有名字。这可是Ruby引擎用的东西啊,我们只要加上一个冒号,就让自己的对象跟Ruby引擎内部使用的对象成邻居了。所以String#intern这个方法叫做intern(内部化)。
.NET Framework中String类也有一个Intern方法,意思是一样一样一样的,在李建忠的经典译本里翻译为“驻留”。
7. 可以用Symbol#all_symbols查看当前定义的全部symbol。可以体验一下自己往符号表中塞一个对象的感觉,想想你写的程序跟Ruby引擎能干一样的事情,应该还是挺爽的。
8. Python中用不着这个,因为字符串是immutable的。放下有用没用不说,有没有办法在Python中intern呢?我还没找到办法。有没有Python牛知道?
补充一下:查到了,Python中做这个事情的函数叫做 intern()。
9. 我觉得Ruby的这个设计是从Perl的glob中简化而来的。Perl中可以用*a得到对应于符号a的glob,那是一个八爪鱼一样的怪物。Ruby也可以很容易的得到symbol table中的对象,不过没有把symbol设计成八爪鱼。
10. 还有一些小问题没搞清楚,比如:name跟@name是什么关系。attr_reader :name,实际上是给attr_reader方法传了一个symbol作为参数,前者要通过这个symbol找到@name变量,是不是'@' + :name.id2name这么简单?大概可以去看看source了。
分享到:
相关推荐
解释的不错,应该明确了不少 ruby symbol详解 起因 最近在学习ruby on rails,的确是一个优秀的数据库开发框架。但在过程中,发现在视图文件夹中的rhtml文件里有大量的类似于以下的语句: <td><%= link_...
New Relic - 找到并修复Ruby错误使用New Relic的应用程序监控和故障诊断
1. 什么是Ruby?简要介绍Ruby的特点和主要用途。 2. Ruby中的变量声明和赋值规则是什么?请说明Ruby变量声明和赋值的语法。 3. Ruby中常用的数据类型有...11. 什么是Ruby中的符号(Symbol)?请说明在Ruby中符号的作用
The new edition of this book provides the same excellent introduction to Ruby as the previous editions plus updates for the newest version of Ruby 2.3 which includes new garbage collection support of ...
ruby语法基础教程,比较全的。 目 录 Ruby语言 1 Grant Ren 1 第一部分 Ruby语言基础 8 第一章 Ruby语言概述 8 §1.1 Ruby的历史 8 §1.2 Ruby名字的由来 8 §1.3 Ruby的特点 8 §1.4 Ruby和Python的比较 9 第二章...
2. 符号(Symbol)的概念及作用,如用于Hash的键。 3. Ruby中"一切皆对象"的理念,数字、布尔值、类、nil等都是对象。 4. Ruby中"一切皆表达式"的设计,控制语句、方法定义等都会返回值。 5. Ruby中"一切皆方法调用",...
符号字符串symbol-fstring是Ruby扩展,提供对符号内部字符串表示形式的访问。为什么? 在Ruby中,许多API倾向于接受符号,但是会定期在内部将其转换为字符串。 典型的示例是ActiveSupport::...
Symbol Decoration gem 提供了符号方法扩展来实现 DSL,例如where(:field.in => [1,2,3]) 。 目标是允许不同的 ORM(例如 Mongoid 和 NoBrainer)共存,因为这两个 ORM 都需要此类功能。 用法 在以下示例中: where...
For-Rails-Beginners::Japanese_symbol_for_beginner:Ruby on Rails的初学者有福了
这是一个简单的Ruby脚本,它打开文件夹路径中给定类型的所有文件,然后计算所有符号。 这可用于通知顶部如何安排可编程键盘。 具体来说,我曾经用来更新固件。 安装 该程序需要编程语言,并且是唯一的依赖项。 该...
Redis排行榜基础演示Ruby on Rails 展示redis如何与Ruby on Rails一起使用。屏幕截图怎么运行的?1.数据的存储方式: 公司数据存储在如下所示的哈希中: HSET "company:AAPL" symbol "AAPL" market_cap ...
Dio或“ Dive Into Objects”(Dive Into Objects)是用于Ruby对象的包装器,这些对象没有定义模式匹配接口,但是具有使它们能够实现其近似的方法: Dio [ 1 ] in { succ : { succ : { succ : 4 } } }# => true ...
Symbol atom Exrb::NewReference或Exrb::Reference reference Exrb::Port port Exrb::Pid pid Exrb::Tuple tuple Hash map Exrb::ErlangNil [] Array `清单 Exrb::ImproperList improper_list utf...
ib-api Ruby与Interactive Brokers的TWS API的接口 重新实现ib-ruby的基本功能 文档: : (正在进行中) ib-ruby提供对Interactive Broker的TWS-API接口的模块化访问。 ib-api为低级TWS API调用提供了... new symbol
IB-Ruby使用相同的概念来组织和优化运营问题,并支持研究和系统的交易工作。 列表按Enumerator进行组织,以扩展其使用范围。 该功能完全存在于文件系统中,不需要数据库,不涉及任何进一步的依赖关系。 默认情况下...
自动检查器会使用许多不同版本的NodeJS,Ruby,Java和许多其他语言来测试您的库。 创建它的目的是为了确保您的库在语言运行库的许多版本中均可轻松运行。 与CI配合也很好! () (现在,将来可以使用NodeJS项目...
Symbol 如果您想增加对其他类型的支持,请随时使用以下准则创建问题或打开请求请求。 用法 例 {-# LANGUAGE OverloadedStrings #-} module Main where import Data.Ruby.Marshal import Data.ByteString ( Byte...
pangu.rb Paranoid text spacing for good readability, to automatically insert whitespace between CJK (Chinese, Japanese, Korean), half-width English, digit and symbol characters. JavaScript version: ...
Flexibility是 ruby 类的混合体,它允许您轻松地#define方法,这些方法可以混合使用位置和关键字参数。 例如,假设我们定义 class Banner include Flexibility define ( :show , message : [ required , ...