• 2007-05-21

    ID和Symbol(一)

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/5465458.html

    ID和Symbol是Ruby中同字符串相关的两个东西,他们的存在是为了一个共同的目的:替代字符串。因为字符串的比较通常代价不菲,以ID和Symbol代替字符串可以提高字符串比较的性能,所以,用到字符串比较的地方都可以选择用ID或是Symbol作为替代品,比如,做Hash表的键值。不同的是,ID用在C层次,而Symbol用在Ruby层次。

    下面是ID的定义:
    typedef unsigned long ID;
    (ruby.h)

    至于Symbol,我在《管窥Ruby——对象基础》说过,在Ruby实现中,VALUE是所有Ruby对象的根基,Symbol也不例外。不同于字符串等类型,我们可能并不会在Ruby实现的源码中看到Symbol类型的定义,它走了与整数类似的一条路,也就是说,它是内嵌在VALUE中的。下面是判断一个VALUE是否为SYMBOL的方法。

    #define SYMBOL_FLAG 0x0e
    #define SYMBOL_P(x) (((VALUE)(x)&0xff)==SYMBOL_FLAG)
    (ruby.h)

    从上面的两段代码,我们不难发现,ID和Symbol实际上都是一个整数。它们都唯一对应着一个字符串,不同的是,ID对应着C的字符串,Symbol对应着Ruby的字符串。既然它们都对应着字符串,那么,它们之间便也顺理成章的存在着一些对应关系。事实上,它们之间的距离比我们想象的还要近,在Ruby的实现中,它们之间是可以直接转换的。

    #define ID2SYM(x) ((VALUE)(((long)(x))<<8|SYMBOL_FLAG))
    #define SYM2ID(x) RSHIFT((unsigned long)x,8)

    这里,RSHIFT是一个平台相关的右移实现。从这里我们不难发现,ID和Symbol之间就是一一对应的。

    那它们是如何对应到字符串的呢?ID和C字符串之间的对应是由下面两个函数完成的:
    ID rb_intern(const char *name);
    char* rb_id2name(ID id);
    (parse.c)

    其中,rb_inern完成C字符串到ID的映射,rb_id2name完成ID到C字符串的映射。其实现很简单,基本上就是一个在hash表中查找,如果没有,便创建一个。

    那么Symbol和Ruby字符串是如何对应的呢?看一下Symbol的to_s方法就知道了。这个方法是这样定义出来的。
    rb_define_method(rb_cSymbol, "to_s", sym_to_s, 0);
    (object.c)

    它实现于sym_to_s方法中。

    static VALUE
    sym_to_s(sym)
        VALUE sym;
    {
        return rb_str_new2(rb_id2name(SYM2ID(sym)));
    }
    (object.c)

    看到了吧!先由Symbol转成ID,再由ID对应到C字符串,最后由C字符串生成Ruby的字符串。绕了好大的一个圈子,所以,Symbol与其对应的Ruby字符串距离还真的是不近。所以,我前面会说,ID和Symbol之间的距离比想象得要近。
    分享到:

    历史上的今天:

    RubyConf China 2009-05-21
    引用地址: