Ruby之symbol研究

来源:百度文库 编辑:神马文学网 时间:2024/04/28 16:53:41
这是一个研究笔记,主要是为了向同好请教。除了这个开头以外,没有多余的废话,也就免了其他的客套。请大家不要抱怨可读性不好。
1. 在一个名字或者字符串前面加上冒号,得到一个symbol对象。还可以通过String#to_sym、Fixnum#to_sym和String#intern得到。
2. 一般用symbol做hash的key,号称是为了节省内存,提高执行效率。
3. 为什么可以节省内存?Ruby中的String是可变对象,这一点跟Java、C#、Python都不一样。注意跟某些C++标准库中的COW的basic_string也不一样。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了。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1255966
[点击此处收藏本文]   孟岩发表于 2006年09月20日 22:56:00
免费Java实战训练营
IDG投资,直接引进北美IT技术成立的培训中心
已经培养了5000多名学员,平均工资4000以上.
http://www.tarena.co...
CSDN读书频道
热点图书连载、试读
名人堂/书友会/特色书架
http://book.csdn.net
Linux百人免费培训,火热报名!
高薪没有捷径——唯有专业享受高薪 签北京就业合同
http://www.uplooking...
Csdn提供的广告  Csdn  Tag

蛋蛋 发表于2006-09-21 00:39:00  IP: 199.246.40.*
孟老大猜错了哈。符号这个概念是Matz从Lisp里引进的。看看Common Lisp的代码,符号无处不在哈。:name和@name和name()的关系是由生成accessor的函数定的,当然不是‘◎’+:name.id2name那么简单。用attr_reader这个函数为例:你把:name这个符号传给attr_reader这个函数。attr_reader根据:name生成name()这个函数(当然真正的实现有很多具体考虑):
def name()
@name
end
不信你可以写自己的attr_reader:
class T
def T.my_attr_reader(name)
class_eval <<-READER
def #{name}
@#{name};
end
READER
end
my_attr_reader :name
def set_name(name)
@name=name;
end
end
t = T.new
t.set_name(‘myan‘);
puts t.name
打印结果就是‘myan‘了。
再说了,attr_reader()不一定只接受符号哈:
attr_reader :name
attr_reader "name"
attr_reader :name.to_s
attr_reader "name".to_sym
都一样的。
思考题:写一个自己的attr_accessor()。
Symbol的意义不仅在于充当一个名字提高点性能,或者充当某个对象的标识符。Symbol对象代表代码解析树里的每个token。比如说def foo; "foo"; end。除了"foo",每个token, def, foo, end什么的都有对应的符号。换句话说,符号为我们提供了进入Ruby解析树的大门。不定哪天我们就可以利用符号直接操作部分解析的Ruby代码了。换句话说,我们可以超越各式eval, 进入人见人耐的macro世界了。慢慢等吧。哈哈哈!
蛋蛋 发表于2006-09-21 00:49:00  IP: 199.246.40.*
P.S., 孟老大的学习还是比较仔细和全面的嘛。CSDN不少大嘴巴"专家"明明缺乏对编程语言的基本了解(比如开篇就来什么被解释的语言就是动态语言一类),却酷耐指点江山,臧否语言,实在应该好好学学孟老大的踏实哈。CSDN办个专题怎么样?普及一下编程语言的基本知识,比如语义的重要性(设计语言总得知道自己要解决什么问题吧?),常见的范式(太多老大言必OO,动不动就叫嚣我的xxx是最好的),类型(工业界和学术界的研究热点哈),常见语言特性(免得universee给closure取了个别名就开始吹嘘自己的2B语言了),常见设计思路什么的。像云风那样认真读过Programming Language Pragmatics这类教材的高手还是应该不少的。程序语言要从娃娃抓起啊。
myan 发表于2006-09-21 07:39:00  IP: 221.218.165.*
to 蛋蛋:
谢谢你的指教。这样看来,attr_reader之类的函数反而是主要接受字符串了。只不过"#{sym}"可以自动对sym调用to_s转型。