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

在Ruby中何时使用符号而不是字符串?,ruby,symbols,Ruby,Symbols,如果脚本中至少有两个相同字符串的实例,我是否应该使用符号?TL;博士 一个简单的经验法则是每次需要内部标识符时都使用符号。对于Ruby

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

TL;博士 一个简单的经验法则是每次需要内部标识符时都使用符号。对于Ruby<2.2,只有在不动态生成符号时才使用符号,以避免内存泄漏

完整答案 不将它们用于动态生成的标识符的唯一原因是内存问题

这个问题很常见,因为许多编程语言没有符号,只有字符串,因此字符串在代码中也用作标识符。你应该担心符号的含义,而不仅仅是你应该使用符号的时候。符号是指标识符。如果你遵循这一理念,你很可能会把事情做好

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

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

您可以获得一个列表,列出已使用命令
Symbol实例化的所有符号。所有符号

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引入了,它释放了动态生成的符号,因此通过动态创建符号生成的内存泄漏不再是一个问题

回答你的问题:

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

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

原因如下:

  • 打印符号将比打印字符串慢,因为它们被强制转换为字符串
  • 拥有大量不同的符号将增加应用程序的总体内存使用量,因为它们永远不会被释放。而且您永远不会同时使用代码中的所有字符串
  • @AlanDert的用例 @AlanDert:如果我在haml代码中多次使用%input{type::checkbox}之类的东西,我应该使用什么作为复选框

    我:是的

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

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

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

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

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

    require 'benchmark'
    require 'haml'
    
    str = Benchmark.measure do
      10_000.times do
        Haml::Engine.new('%input{type: "checkbox"}').render
      end
    end.total
    
    sym = Benchmark.measure do
      10_000.times do
        Haml::Engine.new('%input{type: :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代码进行一些修改才能看到,但是如果您在标识符的概念中继续使用符号,您的应用程序将更快、更可靠。当问题出现时,对其进行基准测试并获得答案。

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

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

    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.
    
    • 使用符号作为散列键标识符

      {key:“value”}

    • 符号允许您以不同的顺序调用该方法

    def写入(文件:,数据:,模式:“ascii”) #为简洁起见,请删除 结束 写入(数据:123,文件:“test.txt”)
    • 冻结以保留为字符串并保存内存

      label='My label'。冻结


    +1。符号和字符串是完全不同的东西。对于使用哪一个符号,真的没有任何混淆,除非他们被教得不好(即“符号只是一个不变的字符串”谬论)。你有一个观点,但是不要回答提出的问题。OP把字符串和符号混淆了,仅仅告诉它是不同的东西是不够的——你应该帮助他理解它们的相似之处以及它们是什么different@J似乎网络上到处都在发生的事情,除非你查阅文档,或者幸运地发现有人关心解释事情的真实情况。让我们不要忘记这是十分之一秒的事实。这都是相对的。有时百分之一百很重要。一百万次迭代中的百分之一秒?如果这是您可用的最佳优化,那么我认为您的程序已经进行了很好的优化。@andrewcockerham您提供的链接不起作用(错误-404)。你必须去掉最后一个 def write(file:, data:, mode: "ascii") # removed for brevity end write(data: 123, file: "test.txt")