何时在 Ruby 中使用符号而不是字符串?

Posted

技术标签:

【中文标题】何时在 Ruby 中使用符号而不是字符串?【英文标题】:When to use symbols instead of strings in Ruby? 【发布时间】:2013-05-13 07:58:00 【问题描述】:

如果我的脚本中至少有两个相同字符串的实例,我应该改用符号吗?

【问题讨论】:

【参考方案1】:

TL;DR

一个简单的经验法则是每次需要内部标识符时都使用符号。对于 Ruby

完整答案

不将它们用于动态生成的标识符的唯一原因是因为内存问题。

这个问题很常见,因为许多编程语言没有符号,只有字符串,因此字符串也被用作代码中的标识符。您应该担心应该是什么符号,而不仅仅是何时应该使用符号。符号是标识符。如果您遵循这一理念,您很可能会做正确的事情。

符号和字符串的实现有几个不同之处。符号最重要的一点是它们是不可变的。这意味着它们的价值永远不会改变。因此,符号的实例化速度比字符串快,并且比较两个符号等一些操作也更快。

符号是不可变的这一事实允许 Ruby 每次引用该符号时都使用同一个对象,从而节省内存。因此,每次解释器读取:my_key 时,它都可以从内存中获取它,而不是再次实例化它。这比每次都初始化一个新字符串要便宜。

您可以使用命令Symbol.all_symbols 获取所有已实例化的符号列表:

symbols_count = Symbol.all_symbols.count # all_symbols is an array with all 
                                         # instantiated symbols. 
a = :one
puts a.object_id
# prints 167778 

a = :two
puts a.object_id
# prints 167858

a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!

puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.

对于 2.2 之前的 Ruby 版本,一旦一个符号被实例化,这块内存将再也不会被释放。释放内存的唯一方法是重新启动应用程序。所以符号使用不当也是造成内存泄漏的主要原因。产生内存泄漏的最简单方法是对用户输入数据使用to_sym 方法,因为该数据总是会改变,因此在软件实例中将永远使用内存的新部分。 Ruby 2.2 引入了symbol garbage collector,它释放了动态生成的符号,因此动态创建符号产生的内存泄漏不再是问题。

回答你的问题:

如果我的应用程序或脚本中至少有两个相同的字符串,我是否必须使用符号而不是字符串?

如果您要查找的是要在代码内部使用的标识符,那么您应该使用符号。如果要打印输出,则应使用字符串,即使它出现多次,甚至在内存中分配两个不同的对象。

原因如下:

    打印符号将比打印字符串慢,因为它们被强制转换为字符串。 拥有大量不同的符号会增加应用程序的整体内存使用量,因为它们永远不会被释放。而且您永远不会同时使用代码中的所有字符串。

@AlanDert 的用例

@AlanDert:如果我在 haml 代码中多次使用 %inputtype: :checkbox 之类的东西,我应该使用什么作为复选框?

我:是的。

@AlanDert:但是要在 html 页面上打印出一个符号,它应该被转换为字符串,不是吗?那么使用它有什么意义呢?

输入的类型是什么?您要使用的输入类型的标识符或要向用户显示的内容?

的确,它会在某个时候变成 HTML 代码,但在您编写该行代码的那一刻,它意味着成为一个标识符 - 它标识您需要什么样的输入字段。因此,它会在您的代码中反复使用,并且始终具有与标识符相同的“字符串”字符,并且不会产生内存泄漏。

也就是说,我们为什么不评估数据以查看字符串是否更快?

这是我为此创建的一个简单基准:

require 'benchmark'
require 'haml'

str = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%inputtype: "checkbox"').render
  end
end.total

sym = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%inputtype: :checkbox').render
  end
end.total

puts "String: " + str.to_s
puts "Symbol: " + sym.to_s

三个输出:

# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68

所以使用 smbols 实际上比使用字符串要快一点。这是为什么?这取决于 HAML 的实现方式。我需要对 HAML 代码进行一些修改才能看到,但是如果您继续在标识符的概念中使用符号,您的应用程序将更快更可靠。当问题出现时,对其进行基准测试并获得答案。

【讨论】:

@andrewcockerham 您提供的链接无效(错误 404)。您必须从链接中删除最后一个 / (在 strings 之后)。这是:www.reactive.io/tips/2009/01/11/the-difference-between-ruby-‌​symbols-and-strings【参考方案2】:

简单地说,符号就是一个名称,由字符组成,但不可变。相反,字符串是字符的有序容器,其内容可以更改。

【讨论】:

+1。符号和字符串是完全不同的东西。 除非他们被教育得很糟糕(即“一个符号只是一个不可变的字符串”的谬误)。 您说得有道理,但不要回答提出的问题。 OP 将字符串与符号混淆,仅说明它是不同的东西是不够的 - 你应该帮助他理解它们的相似之处和不同之处 @JörgWMittag 这似乎正在整个网络上发生,除非您查看文档或有幸找到愿意解释事情真相的人。【参考方案3】:

这是我在 codecademy 找到的一个不错的字符串 vs 符号基准测试:

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]

string_time = Benchmark.realtime do
  1000_000.times  string_AZ["r"] 
end

symbol_time = Benchmark.realtime do
  1000_000.times  symbol_AZ[:r] 
end

puts "String time: #string_time seconds."
puts "Symbol time: #symbol_time seconds."

输出是:

String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.

【讨论】:

我们不要忘记这是十分之一秒的事实。 都是相对的。有时很重要。 超过一百万次迭代的百分之一秒?如果这是您可以使用的最佳优化,我认为您的程序已经得到了很好的优化。【参考方案4】:

使用符号作为哈希键标识符

key: "value"

符号允许你以不同的顺序调用方法

 def write(file:, data:, mode: "ascii")
          # 为简洁起见删除
     结尾
     写(数据:123,文件:“test.txt”)

冻结以保存为字符串并节省内存

label = 'My Label'.freeze

【讨论】:

以上是关于何时在 Ruby 中使用符号而不是字符串?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 ruby​​ 中动态创建大量符号不是一个好主意(对于 2.2 之前的版本)?

将字符串转换为哈希符号的最佳方法

何时在 mysql 中使用 TEXT 而不是 VARCHAR [重复]

ruby:对符号数组进行排序

Ruby如何打印/放置读取的文件内容而不是一堆数字/符号

ruby 中的新日期时间而不是字符串