2017-11-18 148 views
1

试图在这里理解这个问题。我创建了我自己的散列#合并名为hash#my_merge的可以在proc中使用的版本。我想知道是什么做的:试图理解哈希的实现#与Procs合并

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

它看起来像一个三元操作,但newhash [K] =散列[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 

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

回答

4

你误解的路线是三元的,但操作的顺序并不是你所期待的。当红宝石看到:

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]

你可以看到,这是通过查看Operator Precedence文件,里面有红宝石如何看待这条线(很多其他的运营商之间):

,:

修改救援

=,+ =, - =等

所以三元比赋值操作符的优先级更高。


另外值得一提的是newhash[k] = hash[k]是,在Ruby中,真/假声明,因为赋值返回什么分配:

a = 1 # => 1 
a = nil # => nil 

和一切除了false和红宝石nil被视为一个真y值,你可以做的东西,如:

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

if a = nil 
    puts "truthy" 
end 
# outputs nothing 

和适当的圆括号,你可以用它在三元:

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

虽然,这可能会造成混淆(通常是有条件的,你会看到===,而事实上我的红宝石给我警告我在条件下使用=而不是==;但它可以完成

3

虽然当以这种方式使用三元组时,您的核心问题是operator precedence之一,但真正的问题是实际解决方案过于复杂。

如果你是刚刚开始使用Ruby尽最大努力避免类似的事情三元报表,让您的代码简单明了越好。例如,什么是以下DO:

a = nil 

a = true ? :yes : :no 

如果你认为答案是“a分配:yes”那么你正确地读它。如果你觉得“a分配true”那么你就忘记了三元?=用于分配的优先级高,因此首先发生。

,如果你看着它这样同样不会是真实的:

if (a = true) 
    :yes 
else 
    :no 
end 

还是另一种解释清楚奠定:

a = 
    if (true) 
    :yes 
    else 
    :no 
    end 

如果这两个结果是立即明显甚至对编程有基本的了解。如果真的是深夜,并且您正在尝试修复一个错误,并且您无法弄清自己的代码,因为这样做太过复杂,这也可能非常方便。

话虽这么说,一个经过改进的解决方案是这样的:

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] = [ ] }之类的内容,其中第一个设置为静态默认设置,第二个设置为计算的默认设置。如果您没有设置默认值,请不要使用显式初始值设定项。它只是增加了噪音。

您还可以更好地利用红宝石的内置功能,使显著减少你需要临时变量的数量,再加上你真正需要做的,以实现转换这样的工作量。例如,一个小整理,你会得到这样的:

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 implmentation,因为它应该是,不是所有的设置和清理代码。