【问题标题】:Confused about the million ways to reach inside Ruby Hash with the symbol sign对使用符号符号进入 Ruby Hash 内部的百万种方式感到困惑
【发布时间】:2016-10-12 01:52:03
【问题描述】:

我沉浸在数百万种访问 Hash 的方法中。

condition1 = {
    "e" =>1
}

condition2 = {
    "e":1
}

condition3 = {
    :"e" =>3
}


condition4 = {
    e:4
}


condition5 = {  # 100% sure not working because of syntax error
    e=>5
}

condition6 = {  # 100% sure not working because of syntax error
    :e:6
}  

condition7 = {
    :e=>7
}

puts condition1["e"]
puts condition2["e"]
puts condition3[:e]
puts condition4[:e]
puts condition7[:e]

输出

1
                                     (nothing)
3
4
7

第一个问题:对于前 2 个 puts 语句,为什么它适用于第一个而不适用于第二个?在这两个哈希中,我都使用字符串作为键。是因为我在第一个哈希中使用了“=>”,因此字符串实际上被读取为符号?所以它实际上被处理为:“e” => 1?

第二个问题:这就引出了我关于第三个条件的第二个问题,有人告诉我,如果我在引用之前使用 Symbol 符号,那么在后台发生的情况是引用将被忽略。这就是为什么我能够通过简单的 :e 访问它而不带引号。真的吗?但是,如果这是真的,那么第一个问题就会变得更加混乱。如果在第一个条件下,“e”=>1 被评估为:“e”=>1,那么引号不应该被忽略吗?因此最终它被读作:e => 1。那我怎么只能用引号来访问它呢?

第三个问题:那么如何访问第二个条件?我知道不建议使用字符串作为键值。但是,如果在某种情况下我已经用字符串创建了它,我该如何访问它呢?我尝试通过变量名访问它,但它仍然没有输出任何东西。

非常感谢您阅读本文。我知道太在意这些细节绝对不值得。但是我今天遇到了它,如果不弄清楚到底发生了什么,我就无法继续前进!

【问题讨论】:

    标签: ruby hash


    【解决方案1】:

    您需要了解,在 Ruby 中,String 和 Symbol 之间存在巨大差异,并且它们绝不是等价的。你可以转换,有时有些方法会冷漠地采用其中一种,所以这条线可能会模糊,但这是例外,而不是规则。

    因此,"e":e 是不等价的,尤其是在涉及普通哈希时。

    这是 Ruby 在irb 中评估的示例:

    { "e" => 1 } # Classic notation with String key
    # => {"e"=> 1}
    
    { "e": 1 } # New notation with symbol
    # => {:e=>1}
    
    { :"e" =>3 } # Classic notation with alternate Symbol representation
    # => {:e=>3}
    
    { e: 4 } # New notation with Symbol key
    # => {:e=>4}
    
    { e => 5 } # Uses variable e as key, but no such variable exists
    
    { :e:6 } # Invalid notation, not supported by Ruby syntax.
    
    { :e=>7 } # Classic notation with Symbol key
    # => {:e=>7}
    

    在学习时使用irb 之类的工具很重要,它会立即向您展示如何解释任何给定的符号。

    :e:"e" 是等价的,"e".to_sym 也是如此。同样:e.to_s"e" 是等价的,基本上是相反的::e.to_s.to_sym 也是:e,循环往复。

    要访问符号键,:e,字符串"e"。你甚至可以同时拥有一个哈希!

    { :e => 'symbol', 'e' => 'string' }
    

    所以请记住这一点。 Symbol 非常适合您可以控制且经常重复的事情。当您不确定自己在处理什么或键是不可预测的(例如 UUID 值或日期和时间)时,字符串会更好。

    不要忘记哈希可以存储任何东西,所以你甚至可以这样做:

    class E
    end
    
    e = E.new
    
    { E => 'Class', e => 'Instance' }
    

    现在您的 Hash 中既有一个类也有一个实例。

    【讨论】:

      【解决方案2】:

      你的困惑是由两个原因引起的,第一,符号文字的语法,第二,哈希文字的语法。

      符号字面量

      符号的最基本形式是:hello_world

      但是,为了让符号字面量包含特殊字符,ruby 引入了这种语法::'hello-world':"hello-world"

      哈希字面量

      与javascript中只允许字符串作为键的JSON对象不同,ruby允许任何东西作为哈希键,所以哈希的基本形式是

      {
        key1 => value1,
        key2 => value2,
        ...
      }
      

      请注意,key1key2value1value2 只是占位符。你可以用任何东西替换它们,可以是:

      • 文字
      • 变量
      • 常量(包括类和模块)
      • 方法调用

      所以当使用符号字面量作为键,Fixnum 字面量作为值时,基本形式是

      {:e => 1}
      

      {:"e" => 1}
      

      {:'e' => 1}
      

      因为符号字面量经常用作哈希键(因为它们是不可变的),Ruby 引入了这种特定情况的简写版本:将 : 移动到键的尾部,并省略 =>

      {e: 1}
      

      {'e': 1}
      

      {"e": 1}
      

      注意e:之间不能有空格,否则会出现语法错误。

      【讨论】:

        【解决方案3】:

        经验法则

        如果有冒号 (:),它就是一个符号。如果有一个 hashrocket (=>),它就是 hashrocket 左边的任何东西(可以是任何东西)。

        在哈希字面量中声明键

        当我们说“哈希字面量”时,我们指的是用大括号 ({ foo: 1 }) 或作为方法参数 (bar(baz: 2)) 声明哈希的代码。

        有两种方法可以在哈希字面量中声明一个键。

        带有 hashrockets 的密钥 (=>)

        声明密钥的第一种方法是使用 hashrocket (=>)。当你使用 hashrocket 时,key 是它左边的任何值,你可以将 any 类型的对象(或表达式)放在它的左边:

        hashrocket_hash = {
          "I am a String" => 1,
          :I_am_a_Symbol => 2,
          :"I am also a Symbol" => 4,
          /I am a Regexp!/ => 5,
          Kernel => 6,
          if true then "I am also a String" end => 7,
          nil => 8
        }
        
        p hashrocket_hash.keys
        # => [ "I am a String",
        #      :I_am_a_Symbol,
        #      :"I am also a Symbol",
        #      /I am a Regexp!/,
        #      Kernel,
        #      "I am also a String",
        #      nil
        #    ]
        
        p hashrocket_keys.map(&:class)
        # => [ String,
        #      Symbol,
        #      Symbol,
        #      Regexp,
        #      Module,
        #      String,
        #      NilClass
        #    ]
        

        带冒号的键 (:)

        另一种声明密钥的方法是使用冒号 (:)。当您使用冒号时,结果键是 always 一个符号。通常的符号规则适用(在这里阅读非常彻底的答案:What can a ruby symbol (syntax) contain?),除了冒号放在末尾而不是开头:

        colon_hash = {
          I_am_a_Symbol: 9,
          "I am also a Symbol": 10
        }
        
        p colon_hash.keys
        # => [ :I_am_a_Symbol,
        #      :"I am also a Symbol" ]
        
        p colon_hash.keys.map(&:class)
        # => [ Symbol,
        #      Symbol ]
        

        访问哈希值

        访问哈希值没有特殊规则。如果要访问其键为符号的值,则必须使用符号。如果要访问其键为字符串的值,则必须使用字符串。如果钥匙是别的东西,你必须使用那个东西。

        在下面的示例中,请密切注意哪些键后面是冒号,哪些后面是 hashrockets:

        hsh1 = { foo: 1 }
        p hsh1[:foo]   # => 1
        p hsh1[:"foo"] # => 1
        p hsh1["foo"]  # => nil
        
        hsh2 = { "bar": 2 }
        p hsh2[:bar]   # => 2
        p hsh2[:"bar"] # => 2
        p hsh2["bar"]  # => nil
        
        hsh3 = {
          Kernel: 3,
          Kernel => 4
        }
        p hsh3[:Kernel]  # => 3
        p hsh3[Kernel]   # => 4
        p hsh3["Kernel"] # => nil
        

        【讨论】:

        • 非常感谢。我认为“经验法则”几乎回答了我所有的问题。
        猜你喜欢
        • 2016-05-19
        • 2013-12-31
        • 2011-06-12
        • 1970-01-01
        • 2014-06-02
        • 2012-01-08
        • 1970-01-01
        • 2021-12-05
        • 2013-05-11
        相关资源
        最近更新 更多