如何理解Ruby中的符号

Kez*_*zer 81 ruby symbols

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

这是来自链接的引用:

与字符串不同,同名的符号在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
Run Code Online (Sandbox Code Playgroud)

patient1并且patient2都是哈希,这很好. :ruby然而是一个象征.如果我们要输出以下内容:

patient1.each_key {|key| puts key.to_s}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我可以为符号分配值吗?
  • 符号只是一个指向变量的指针吗?
  • 如果符号是全局的,那是否意味着符号总是指向一个东西?

ans*_*hul 60

考虑一下:

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
Run Code Online (Sandbox Code Playgroud)

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


(回应下面的评论)

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

hash1 = { "string" => "value"}
hash2 = { "string" => "value"}
Run Code Online (Sandbox Code Playgroud)

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

hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}
Run Code Online (Sandbox Code Playgroud)

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

  • 符号不包含值,它们是_are_值.哈希包含值. (46认同)
  • 这是存储键/值对的`Hash`(由代码中的{... => ...}创建),而不是`Symbol`s本身.`符号`(例如`:symbol`或`:sym`或`:ruby`)是成对中的键.只作为"哈希"的一部分,他们"指向"任何东西. (5认同)

Seg*_*ult 50

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

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

  • 阅读这些帖子,这个对我来说最有意义.:ruby只是存储在内存中的某个地方,如果我在某处使用"ruby",然后再在某处"ruby",它只是重复.因此,使用符号是减少公共数据重复的一种方法.如你所说,常量字符串.我想有一些潜在的机制会再次找到该符号使用? (2认同)

jcd*_*yer 32

符号:ruby不包含"red""programming".符号:ruby只是符号:ruby.这是你的哈希值,patient1并且patient2每个包含这些值,在每种情况下有相同键指向.

想一想:如果你在圣诞节早晨进入起居室,看到两个带有标签的盒子,上面写着"Kezzer".在它上面有袜子,另一个有煤.你不会感到困惑,并问"Kezzer"如何包含袜子和煤炭,即使它是同一个名字.因为名称不包含(蹩脚)礼物.它只是指着他们.同样,:ruby不包含哈希值中的值,它只是指向它们.

  • 这个答案完全有道理. (2认同)
  • 请把一些这些放入红宝石官方文档中 (2认同)

tad*_*man 26

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

以你的例子为例:

patient1 = { :ruby => "red" }
Run Code Online (Sandbox Code Playgroud)

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

另一种写作方式是:

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

puts patient1[:ruby]
# 'red'
Run Code Online (Sandbox Code Playgroud)

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

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

即使值相同,每个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
Run Code Online (Sandbox Code Playgroud)

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

[ :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
Run Code Online (Sandbox Code Playgroud)

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

[ "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
Run Code Online (Sandbox Code Playgroud)

同样,每次从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
Run Code Online (Sandbox Code Playgroud)

您可以将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=, ...
Run Code Online (Sandbox Code Playgroud)

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


Chu*_*uck 15

符号不是指针.它们不包含值.符号就是这样.:ruby是符号:ruby,这就是它的全部.它不包含值,它不做任何事情,它只是作为符号存在:ruby.符号:ruby是一个值,就像数字1一样.它不会指向另一个值,而不是数字1.


mea*_*gar 12

patient1.each_key {|key| puts key.to_s}
Run Code Online (Sandbox Code Playgroud)

然后将输出什么?"红色",还是"编程"?

它们都不会输出"红宝石".

你混淆了符号和哈希.它们没有关联,但它们在一起很有用.有问题的符号是:ruby; 它与散列中的值无关,它的内部整数表示将始终相同,并且它的"值"(转换为字符串时)将始终为"ruby".


Mar*_*Fox 10

简而言之

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

为什么:红色比"红色"好

在动态面向对象语言中,您可以使用可读引用创建复杂的嵌套数据结构.散列是一种常见的情况,你值映射到唯一键-独特的,至少,每个实例.每个哈希不能有多个"红色"密钥.

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

优点

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

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

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

摘要

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