Ruby 试图理解Hash#merge与Procs的实现

Ruby 试图理解Hash#merge与Procs的实现,ruby,hash,merge,Ruby,Hash,Merge,试图理解这个问题。我正在创建一个名为hash#my#u merge的hash#merge的我自己的验证,它可以接受一个进程。我试图了解它的功能: self.each do |k,v| newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v end 它看起来像一个三元运算,但newhash[k]=hash[k]不是真/假语句吗?提示及问题的其余部分如下: class Hash # Hash#merge takes a proc that accepts t

试图理解这个问题。我正在创建一个名为hash#my#u merge的hash#merge的我自己的验证,它可以接受一个进程。我试图了解它的功能:

self.each do |k,v|
  newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v
end
它看起来像一个三元运算,但newhash[k]=hash[k]不是真/假语句吗?提示及问题的其余部分如下:

class Hash
# Hash#merge takes a proc that accepts three arguments: a key and the two
# corresponding values in the hashes being merged. Hash#merge then sets that
# key to the return value of the proc in a new hash. If no proc is given,
# Hash#merge simply merges the two hashes.
#
# Write a method with the functionality of Hash#merge. Your Hash#my_merge 
method
# should optionally take a proc as an argument and return a new hash. If a 
proc
# is not given, your method should provide default merging behavior. Do not 
use
# Hash#merge in your method.

  def my_merge(hash, &prc)
    prc ||=Proc.new{|k,oldval,newval|}
    newhash=Hash.new

    self.each do |k,v|
      newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v
    end

    hash.each do |k,v|
      newhash[k]=v if newhash[k].nil?
    end
    newhash
  end
end

任何帮助都将不胜感激。谢谢

你的误解是一条三元线,但操作顺序不是你所期望的。当Ruby看到:

newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v
它将其视为

newhash[k]= (hash[k] ? prc.call(k,v,hash[k]): v)
因此,如果
hash[k]
是真值-y,则调用proc并将结果分配给
newhash[k]
;否则,将
v
分配给
newhash[k]

通过查看文件,您可以看到Ruby是这样看待这一行的,该文件包含(在一堆其他操作符中):

?,:

改装救援

=、+=、-=,等等

因此,三元运算符的优先级高于赋值运算符


另外值得注意的是,在Ruby中,
newhash[k]=hash[k]
是一个true/false语句,因为赋值返回被赋值的内容:

a = 1 # => 1
a = nil # => nil
Ruby中除了
false
nil
之外的所有内容都被视为一个truth-y值,您可以执行以下操作:

if a = 2
  puts "truthy"
end
# outputs 'truthy'

通过适当的括号,您可以在三元结构中使用它:

(a = 1) ? 'truthy' : 'falsey' # => 'truthy'
(a = nil) ? 'truthy' : 'falsey' # => 'falsey'

不过,这可能会让人困惑(通常在条件中你会看到
==
而不是
=
,事实上,我的Ruby给了我一个警告,我在条件中使用的是
=
,而不是
=
);但是这是可以做到的,你的误解是三元的,但是操作的顺序不是你所期望的。当Ruby看到:

newhash[k]=hash[k] ? prc.call(k,v,hash[k]): v
它将其视为

newhash[k]= (hash[k] ? prc.call(k,v,hash[k]): v)
因此,如果
hash[k]
是真值-y,则调用proc并将结果分配给
newhash[k]
;否则,将
v
分配给
newhash[k]

通过查看文件,您可以看到Ruby是这样看待这一行的,该文件包含(在一堆其他操作符中):

?,:

改装救援

=、+=、-=,等等

因此,三元运算符的优先级高于赋值运算符


另外值得注意的是,在Ruby中,
newhash[k]=hash[k]
是一个true/false语句,因为赋值返回被赋值的内容:

a = 1 # => 1
a = nil # => nil
Ruby中除了
false
nil
之外的所有内容都被视为一个truth-y值,您可以执行以下操作:

if a = 2
  puts "truthy"
end
# outputs 'truthy'

通过适当的括号,您可以在三元结构中使用它:

(a = 1) ? 'truthy' : 'falsey' # => 'truthy'
(a = nil) ? 'truthy' : 'falsey' # => 'falsey'

不过,这可能会让人困惑(通常在条件中你会看到
==
而不是
=
,事实上,我的Ruby给了我一个警告,我在条件中使用的是
=
,而不是
=
);但是这是可以做到的,当你的核心问题是以那种方式使用三元时,真正的问题是实际解决方案过于复杂

如果您刚刚开始使用Ruby,请尽量避免使用诸如三元语句之类的语句,以使代码尽可能简单明了。例如,以下各项的作用是什么:

a = nil

a = true ? :yes : :no
如果您认为答案是“
a
已分配
:yes
”,那么您的阅读是正确的。如果您认为“
a
被赋值
true
”,那么您忘记了三元
的赋值优先级高于
=
,因此它首先发生

如果你这样看,情况就不一样了:

if (a = true)
  :yes
else
  :no
end
或者另一种解释明确指出:

a =
  if (true)
    :yes
  else
    :no
  end
即使对编程有了基本的了解,这两者的结果也会立即显现出来。如果真的很晚了,而且你正试图修复一个bug,但是你自己的代码太复杂了,你无法理解,那么这也很方便

也就是说,经过返工的解决方案如下所示:

class Hash
  def my_merge(hash)
    # Figure out all the keys that might show up in this merge in advance.
    keys = (self.keys + hash.keys).uniq
    newhash = { }

    # Try all possible keys and evaluate what the result should be
    keys.each do |k|
      newhash[k] =
        if (block_given?)
          yield(k, self[k], hash[k])
        elsif (self.has_key?(k))
          self[k]
        else
          hash[k]
        end
    end

    newhash
  end
end
您可以利用
if
在Ruby中实际返回值的方式来简化这个问题,并保持内部逻辑非常清晰

需要注意的一点是,当您使用
Hash.new
时,您可能指的是
{}
。正式声明保留用于
Hash.new(0)
Hash.new{| h,k | h[k]=[]}
等内容,其中第一个设置静态默认值,第二个设置计算默认值。如果没有设置默认值,请不要使用显式初始值设定项。这只会增加噪音

您还可以更好地利用Ruby的内置功能,这些功能可以显著减少所需的临时变量数量,以及实现这样的转换所需的实际工作量。例如,稍微整理一下,你会发现:

class Hash
  def my_merge(hash)
    (self.keys + hash.keys).uniq.map do |k|
      [
        k,
        if (block_given?)
          yield(k, self[k], hash[k])
        elsif (self.has_key?(k))
          self[k]
        else
          hash[k]
        end
      ]
    end.to_h
  end
end

这是相当精简的,最大的代码块是
merge
实现,应该是这样的,而不是所有的设置和清理代码。

虽然您的核心问题是以这种方式使用三元结构,但真正的问题是实际解决方案过于复杂

如果您刚刚开始使用Ruby,请尽量避免使用诸如三元语句之类的语句,以使代码尽可能简单明了。例如,以下各项的作用是什么:

a = nil

a = true ? :yes : :no
如果您认为答案是“
a
已分配
:yes
”,那么您的阅读是正确的。如果您认为“
a
被赋值
true
”,那么您忘记了三元
的赋值优先级高于
=
,因此它首先发生

如果你这样看,情况就不一样了:

if (a = true)
  :yes
else
  :no
end
或者另一种解释明确指出:

a =
  if (true)
    :yes
  else
    :no
  end
即使对编程有了基本的了解,这两者的结果也会立即显现出来。如果真的很晚了,而且你正试图修复一个bug,但是你自己的代码太复杂了,你无法理解,那么这也很方便

话虽如此,一个重新设计的