首页 文章

如何理解Ruby中的符号

提问于
浏览
77

尽管阅读了“Understanding Ruby Symbols”,但在使用符号时,我仍然对内存中数据的表示感到困惑 . 如果一个符号(其中两个包含在不同的对象中)存在于同一个内存位置,那么它们如何包含不同的值?我原本期望相同的内存位置包含相同的值 .

这是来自链接的引用:

与字符串不同,同名的符号在ruby会话期间初始化并仅存在于内存中一次

我不明白它如何设法区分同一内存位置中包含的值 .

考虑这个例子:

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094

patient1patient2 都是哈希,没关系 . :ruby 然而是一个符号 . 如果我们要输出以下内容:

patient1.each_key {|key| puts key.to_s}

然后将输出什么? "red" ,或 "programming"

忘记哈希一秒钟,我认为符号是一个指向值的指针 . 我的问题是:

  • 我可以为符号分配值吗?

  • 符号只是一个指向变量的指针吗?

  • 如果符号是全局的,那是否意味着符号总是指向一个东西?

11 回答

  • 57

    您可能会假设您所做的声明将Symbol的值定义为不同于它的值 . 事实上,Symbol只是一个保持不变的“内化”字符串值 . 这是因为它们使用经常使用的简单整数标识符存储,因为它比管理大量可变长度字符串更有效 .

    以你的例子为例:

    patient1 = { :ruby => "red" }
    

    这应该理解为:“声明一个变量patient1并将其定义为Hash,并在此存储中键下的值为'red'(符号'ruby')”

    另一种写作方式是:

    patient1 = Hash.new
    patient1[:ruby] = 'red'
    
    puts patient1[:ruby]
    # 'red'
    

    当您进行任务时,您获得的结果与您首先指定的结果相同并不令人惊讶 .

    符号概念可能有点令人困惑,因为它不是大多数其他语言的特征 .

    即使值相同,每个String对象也是不同的:

    [ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
      puts v.inspect + ' ' + v.object_id.to_s
    end
    
    # "foo" 2148099960
    # "foo" 2148099940
    # "foo" 2148099920
    # "bar" 2148099900
    # "bar" 2148099880
    # "bar" 2148099860
    

    具有相同值的每个符号都指向同一个对象:

    [ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
      puts v.inspect + ' ' + v.object_id.to_s
    end
    
    # :foo 228508
    # :foo 228508
    # :foo 228508
    # :bar 228668
    # :bar 228668
    # :bar 228668
    

    将字符串转换为符号会将相同的值映射到相同的唯一符号:

    [ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
      v = v.to_sym
      puts v.inspect + ' ' + v.object_id.to_s
    end
    
    # :foo 228508
    # :foo 228508
    # :foo 228508
    # :bar 228668
    # :bar 228668
    # :bar 228668
    

    同样,每次从Symbol转换为String都会创建一个不同的字符串:

    [ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
      v = v.to_s
      puts v.inspect + ' ' + v.object_id.to_s
    end
    
    # "foo" 2148097820
    # "foo" 2148097700
    # "foo" 2148097580
    # "bar" 2148097460
    # "bar" 2148097340
    # "bar" 2148097220
    

    您可以将Symbol值视为从内部哈希表中绘制,您可以使用简单的方法调用查看已编码为符号的所有值:

    Symbol.all_values
    
    # => [:RUBY_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :@seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...
    

    当您通过冒号或使用.to_sym定义新符号时,此表将增长 .

  • 47

    patient1.each_key {| key | put key.to_s}
    然后将输出什么? “红色”,还是“编程”?

    它们都不会输出“红宝石” .

    你__95872_吨相关,但它们在一起很有用 . 有问题的符号是 :ruby ;它与散列中的值无关,而's internal integer representation will always be the same, and it' s "value"(转换为字符串时)将始终为"ruby" .

  • 3

    考虑一下:

    x = :sym
    y = :sym
    (x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true
    
    x = "string"
    y = "string"
    (x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false
    

    因此,无论您创建一个符号对象,只要它的内容相同,它就会引用内存中的同一个对象 . 这不是问题,因为符号是immutable object . 字符串是可变的 .


    (回应下面的评论)

    在原始文章中,值不存储在符号中,而是存储在哈希中 . 考虑一下:

    hash1 = { "string" => "value"}
    hash2 = { "string" => "value"}
    

    这会在内存中创建六个对象 - 四个字符串对象和两个哈希对象 .

    hash1 = { :symbol => "value"}
    hash2 = { :symbol => "value"}
    

    这只在内存中创建了五个对象 - 一个符号,两个字符串和两个哈希对象 .

  • 30

    In short

    符号解决了创建人类可读,不可变表示的问题,这些表示还具有运行时查找比字符串更简单的优点 . 可以把它想象成可以重复使用的名称或标签 .

    Why :red is better than "red"

    在动态面向对象语言中,您可以使用可读引用创建复杂的嵌套数据结构 . The hash is a common use case 将值映射到唯一键 - 在每个实例中至少是唯一的 . 每个哈希不能有多个"red"密钥 .

    但是,使用数字索引而不是字符串键会更高效 . 因此,引入符号作为速度和可读性之间的折衷 . 符号解析比等效字符串容易得多 . 通过人类可读且易于运行时解析符号是动态语言的理想补充 .

    Benefits

    由于符号是不可变的,因此可以在运行时共享它们 . 如果两个散列实例对红色项具有共同的词典或语义需求,则符号:red将使用大约一半的内存,而字符串“red”将需要两个散列 .

    因为:red总是回退到内存中的相同位置,它可以在几百个哈希实例中重复使用而内存几乎没有增加,而使用“red”会增加内存成本,因为每个哈希实例都需要存储可变字符串创建 .

    不确定Ruby实际上如何实现符号/字符串,但显然符号在运行时提供的实现开销较少,因为它是一个固定的表示 . 加号符号比引用的字符串少一个字符,而较少的键入是真正的Rubyist的永恒追求 .

    Summary

    使用符号:red,由于字符串比较操作的成本以及将每个字符串实例存储在内存中的需要,您可以获得字符串表示的可读性,同时减少开销 .

  • 0

    “指向”由哈希控制,而不是由符号控制 .

    符号是它自己的独特之处,就像实例对象一样 . 或任何其他数据类型 .

    (实际上,您可以将符号视为字符串常量,而由与符号相同的字符组成的字符串将是String类的实例对象 . 这样做的好处是减少了创建的对象数量,减少了内存使用量,减少了由于引用不同的对象而不是相同的符号对象而导致的时髦行为 .

    使用 :ruby"red""programming"patient1patient2

    哈希 patient1 查看符号 :ruby ,然后去,“哦,这对应于 "red"

    哈希 patient2 查看符号 :ruby ,然后去,“哦,这对应于 "programming"

  • 12

    我建议阅读Wikipedia article on hash tables - 我想它会帮助你理解 {:ruby => "red"} 的真正含义 .

    另一项可能有助于您了解情况的练习:考虑 {1 => "red"} . 从语义上讲,这并不意味着“将 1 的值设置为 "red" ", which is impossible in Ruby. Rather, it means "创建Hash对象,并将值 "red" 存储为键 1 .

  • -2

    当我这样想时,我能够生成符号 . Ruby字符串是一个具有大量方法和属性的对象 . 人们喜欢使用字符串作为键,当字符串用于键时,则不使用所有这些额外的方法 . 因此,他们制作符号,这些符号是删除了所有功能的字符串对象,除了它是一个好键所需要的 .

    只需将符号视为常量字符串即可 .

  • 3

    符号 :ruby 不包含 "red""programming" . 符号 :ruby 只是符号 :ruby . 它是你的哈希值 patient1patient2 ,它们每个都包含这些值,在每种情况下由相同的键指向 .

    想一想:如果你在圣诞节早晨进入客厅,看到两个带有标签的盒子,上面写着"Kezzer" . 在它上面有袜子,另一个有煤 . 你__95858_ t包含(蹩脚)礼物 . 它只是指着他们 . 同样, :ruby 不包含哈希值中的值,只是指向它们 .

  • 15

    我是Ruby的新手,但我想(希望?)这是一个看待它的简单方法......

    符号不是变量或常量 . 它不代表或指向一个值 . 符号是一个值 .

    它只是一个没有对象开销的字符串 . 文字只有文字 .

    所以这:

    "hellobuddy"
    

    与此相同:

    :hellobuddy
    

    除非你做不到,例如:hellobuddy.upcase . 它是字符串值,只有字符串值 .

    同样,这个:

    greeting =>"hellobuddy"
    

    与此相同:

    greeting => :hellobuddy
    

    但是,再次,没有字符串对象开销 .

  • 10

    patient1 = {:ruby =>“red”}
    patient2 = {:ruby =>“编程”}

    patient1.each_key {| key | put key.object_id.to_s}
    3918094
    patient2.each_key {| key | put key.object_id.to_s}
    3918094
    病人1和病人2都是哈希,这很好 . :红宝石然而是一个象征 . 如果我们要输出以下内容:patient1.each_key {| key | put key.to_s}
    然后将输出什么? “红色”,还是“编程”?

    当然,也不是 . 输出将是 ruby . 其中,BTW,您可以在比输入问题所花费的时间更短的时间内找到,只需将其输入IRB即可 .

    为什么会 redprogramming ?符号总是评估自己 . 符号 :ruby 的值是符号 :ruby 本身,符号 :ruby 的字符串表示形式是字符串值 "ruby" .

    [BTW: puts 总是将其参数转换为字符串,无论如何 . 没有必要在上面调用 to_s . ]

  • 26

    符号不是指针 . 它们不包含值 . 符号就是这样 . :ruby 是符号 :ruby 并且's all there is to it. It doesn' t包含一个值,它不执行任何操作,它只是作为符号 :ruby 存在 . 符号 :ruby 是一个与数字1相同的值 . 它不会指向另一个值,而不是数字1 .

相关问题