ruby 生成哈希值_Ruby哈希值和可变的默认值

ruby 生成哈希值

Ruby’s Hash object is an associative data structure used to store key-value pairs. Many languages have objects that serve a similar purpose: Python has dictionaries, JavaScript has Maps, Java has HashMaps, and so on. Ruby’s Hash object is pretty simple to use.

Ruby的Hash对象是用于存储键值对的关联数据结构。 许多语言的对象都具有类似的目的:Python具有字典,JavaScript具有Maps,Java具有HashMaps,依此类推。 Ruby的Hash对象非常易于使用。

my_hash = {}
my_hash[0] = 'Hello!'
puts my_hash[0]
#=> Hello!

This works just fine. We can also change the value associated with a key that already exists in the hash.

这样很好。 我们还可以更改与哈希中已存在的键关联的值。

my_hash[0] << "I've been mutated!"
puts my_hash[0]
#=> Hello! I've been mutated!

The [] operator is just syntactic sugar for the #[] method defined in the Hash class. Here is the description from the Ruby Docs:

[]运算符只是Hash类中定义的#[]方法的语法糖。 这是Ruby Docs中的描述:

hsh[key] → value

hsh [key]→值

Retrieves the value object corresponding to the key object. If not found, returns the default value.

检索与对象相对应对象。 如果找不到,则返回默认值。

The last sentence in this description deserves a bit more explanation.

此描述中的最后一句话值得更多解释。

my_hash = {}
my_hash[:exists] = 0
my_hash[:exists] += 1
puts my_hash[:exists]
#=> 1my_hash[:does_not_exist] += 1
#=> NoMethodError (undefined method `+' for nil:NilClass)

Hashes have a default value, which is the value returned when accessing keys that do not exist in the hash. In the code above, we are attempting to access the key :does_not_exist and then call the #+ method on the value associated with that key. The error message tells us that there is no #+ method defined for the value that is returned by accessing this key. This is because hashes initialized using the code hash_name = {} will have their default value set to nil.

哈希具有默认值,该默认值是访问哈希中不存在的键时返回的值。 在上面的代码中,我们尝试访问键:does_not_exist ,然后对与该键关联的值调用#+方法。 该错误消息告诉我们,没有为通过访问此键返回的值定义#+方法。 这是因为使用代码hash_name = {}初始化的哈希将其默认值设置为nil

p my_hash[:does_not_exist]
#=> nil

We can set the default value ourselves by using the Hash::new method:

我们可以使用Hash::new方法Hash::new设置默认值:

my_hash = Hash.new(0)
my_hash[:does_not_exist] += 1
puts my_hash[:does_not_exist]
#=> 1

None of this is particularly mind-blowing, but what happens if we try to run this code:

这些都不是特别令人神往的,但是如果我们尝试运行以下代码会发生什么:

my_hash = Hash.new([])
my_hash[:does_not_exist] << 'Uh oh.'
p my_hash
#=> {}p my_hash[:does_not_exist]
#=> ["Uh oh."]

Wait a minute. The p method uses the #inspect method to print a human-readable representation of the object passed in. So you may have expected to see {:does_not_exist=>["Uh oh."]}, but instead it looks like the hash is still empty. But if we retrieve the value associated with the :does_not_exist symbol (which, as its name implies, does not exist), we get the value we expected.

等一下。 p方法使用#inspect方法来打印传入的对象的人类可读表示。因此,您可能希望看到{:does_not_exist=>["Uh oh."]} ,但是看起来像是哈希仍然是空的。 但是,如果检索与:does_not_exist符号关联的值(顾名思义,该符号不存在),我们将获得预期的值。

p my_hash[:does_not_exist_either]
#=> ["Uh oh."]

Alright, this is weird. It looks like the hash has key-value pairs that we didn’t even specify.

好吧,这很奇怪。 看起来散列具有我们甚至未指定的键值对。

There is actually a reasonable explanation for this, but it requires careful attention to the description of the Hash::new method in the Ruby Docs.

实际上对此有一个合理的解释,但是需要仔细注意Ruby Docs中对Hash::new方法的描述。

new → new_hash

新→new_hash

new(obj) → new_hash

new(obj)→new_hash

Returns a new, empty hash. If this hash is subsequently accessed by a key that doesn’t correspond to a hash entry, the value returned depends on the style of new used to create the hash. In the first form, the access returns nil. If obj is specified, this single object will be used for all default values.

返回一个新的空哈希。 如果此哈希随后被与哈希条目不对应的键访问,则返回的值取决于用于创建哈希new样式 在第一种形式中,访问返回nil 如果指定了obj,则此单个对象将用于所有默认值

The last sentence is key. When we initialize a hash using Hash.new([]), we are passing in an array object. This single object will be used as the default value for the hash. We can prove this by printing a few object ids.

最后一句话是关键。 当我们使用Hash.new([])初始化哈希时,我们传入一个数组对象。 该单个对象将用作哈希的默认值。 我们可以通过打印一些对象ID来证明这一点。

my_hash = Hash.new([])
my_hash[0] << 1
puts my_hash[0].object_id
#=> 180
puts my_hash[:does_not_exist].object_id
#=> 180

When we use the << operator on the second line of this code, we aren’t mutating the hash at all. In fact, we are mutating the default value that the Hash#[] method returns when we attempt to access a key that doesn’t exist. Think carefully about what is happening in this code:

当我们在此代码的第二行使用<<操作符时,我们根本就不会改变哈希值。 实际上,当我们尝试访问不存在的键时,我们正在改变Hash#[]方法返回的默认值。 仔细考虑一下这段代码中发生了什么:

Line 1: We invoke the Hash::new method, passing in an empty array object as the argument. This returns a new hash object which will use the empty array object as the default value.

第1行:我们调用Hash::new方法,传入一个空数组对象作为参数。 这将返回一个新的哈希对象,它将使用空数组对象作为默认值。

Line 2: We use the [] operator on the hash, which is syntactic sugar for the Hash#[] method. We pass in the integer 0 as the argument for this method. Because there is no key with the value 0 in our hash, the method returns the default value (our single empty array object). We then use the << operator to invoke the Array#<< method to mutate that empty array object, not the hash! We pass an integer with value 1 into the #<< method, which appends that integer onto the end of our formerly empty array. The key here is that our hash is left unchanged by this code.

第2行:我们在哈希上使用[]运算符,它是Hash#[]方法的语法糖。 我们传入整数0作为此方法的参数。 由于哈希中没有键值为0键,因此该方法返回默认值(我们的单个空数组对象)。 然后,我们使用<<操作符调用Array#<<方法来变异该空数组对象,而不是哈希! 我们将一个值为1的整数传递给#<<方法,该方法将该整数附加到我们以前为空的数组的末尾。 关键是此代码不会使我们的哈希保持不变

Consider this code:

考虑以下代码:

my_array = []
my_hash = Hash.new(my_array)
my_hash[0] << 1p my_hash
#=> {}
p my_array
#=> [1]p my_array.object_id
#=> 200
p my_hash[:does_not_exist].object_id
#=> 200

This code is fundamentally the same as the previous code snippet, except this should make it clear that it is our default value that is being mutated, not the hash.

该代码与前面的代码片段基本相同,不同之处在于,这应该使我们清楚是突变的是我们的默认值,而不是哈希值。

What we’ve discovered here is not unique to arrays as hash values. This same concept applies for all mutable objects.

我们在这里发现的并不是数组作为哈希值所独有的。 同样的概念适用于所有可变对象。

my_string = ''
my_hash = Hash.new(my_string)
my_hash[0] << 'nope.'p my_hash
#=> {}
p my_string
#=> "nope."

So, is there another way to make this work? Can we use a mutable object as a default value and have it work as we originally expected? To answer this question, we have to take another look at the Ruby Docs for the Hash::new method.

那么,还有另一种方法可以使这项工作吗? 我们可以使用可变对象作为默认值,并使它按我们最初的预期工作吗? 要回答这个问题,我们必须再看看Hash::new方法的Ruby Docs

…If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block’s responsibility to store the value in the hash if required.

…如果指定了一个块,它将被哈希对象和键调用,并应返回默认值。 如果需要,将值存储在哈希中是块的责任。

Alright, so we can pass a block into the Hash::new method. That means we can change our code to look like this:

好了,所以我们可以将一个块传递给Hash::new方法。 这意味着我们可以将代码更改为如下所示:

my_hash = Hash.new { |hash, key| hash[key] = [] }
my_hash[0] << 'This works!'
my_hash[1] << 'This works too!'p my_hash
#=> {0=>["This works!"], 1=>["This works too!"]}
p my_hash[0].object_id
#=> 220
p my_hash[1].object_id
#=> 240

So what is happening here? On line 1, we are again invoking the Hash::new method. But this time, we are passing in a block instead of an array object. As the docs state, when we attempt to access a key that does not exist this block will be called. The hash object and the key we are trying to access will be passed as arguments into the block. Within the block, we will associate a new unique array object with the object assigned to key. The return value of the Hash#[]= operator is the value used for the assignment, so our block will also return that same value. This fulfills the obligation to return the default value from the block, as stated in the description of the Hash::new method.

那么这里发生了什么? 在第1行,我们再次调用Hash::new方法。 但是这次,我们传递的是块而不是数组对象。 正如文档所述,当我们尝试访问不存在的密钥时,将调用此块。 我们尝试访问的哈希对象和密钥将作为参数传递到块中。 在该块内,我们将一个新的唯一数组对象与分配给key的对象相关联。 Hash#[]=运算符的返回值是用于赋值的值,因此我们的块也将返回相同的值。 如Hash::new方法的描述中所述,这履行了从块中返回默认值的义务。

There is an arguably simpler solution. We don’t have to pass a block into the Hash::new method if we just don’t try to mutate the default value.

有一种可能更简单的解决方案。 如果我们只是不尝试更改默认值,则不必将块传递给Hash::new方法。

my_hash = Hash.new([])
my_hash[:does_not_exist] += ['Uh oh.']
p my_hash
#=> {:does_not_exist=>["Uh oh."]

Pay close attention to the operators used on line 2. Instead of mutating the array with the << operator, we use the += operator to construct a new string and then assign it to the key :does_not_exist. Our default value is left alone and our hash is correctly mutated.

请密切注意第2行上使用的运算符。我们没有使用<<操作符来对数组进行变异,而是使用+=运算符构造了一个字符串,然后将其分配给键:does_not_exist 。 我们的默认值不理会,我们的哈希值正确变异。

I hope that you found this helpful, or at least somewhat interesting. I ran into this problem and was pretty confused about what was going on. Figuring out why the code wasn’t doing what I expected was a great learning opportunity and reasoning through it was a great opportunity to describe, in detail, exactly what the code I had written was doing. I would like to credit Andrew Marshall for a very helpful explanation on this topic posted on Stack Overflow a few years ago.

我希望您觉得这很有帮助,或者至少有点有趣。 我遇到了这个问题,对发生的事情感到非常困惑。 弄清楚为什么代码没有按照我的预期去做是一个很好的学习机会,而通过它进行推理是一个很好的机会来详细描述我编写的代码在做什么。 我要感谢安德鲁·马歇尔( Andrew Marshall)几年前在Stack Overflow上发布的有关此主题的非常有用的解释

翻译自: https://medium.com/@lawnfurniture_20661/ruby-hashes-and-mutable-default-values-df8841eba25b

ruby 生成哈希值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值