我通过反复试验解决了这个问题,但是我对splat运算符和pp
方法如何一直给我一个不同于我认为的对象的理解显然存在差距。我想了解这个差距,并找出合并散列数组的更好方法。我也希望能够在未来更有效地调试这类事情。
首先是代码示例和调试步骤。我的半满意解决方案和更详细的问题都在底部。
我正在使用MRI Ruby 2.6.2。鉴于Foo类,我希望Foo #windows返回一个合并的哈希值。这是该类的最小示例:
class Foo
attr_reader :windows
def initialize
@windows = {}
end
def pry
{ pry: "stuff pry\r" }
end
def irb
{ irb: "stuff irb\r" }
end
def windows= *hashes
@windows.merge! *hashes
end
end
foo = Foo.new
foo.windows = foo.pry, foo.irb
但是,尝试分配给foo.windows(或者甚至试图用foo.windows= foo.pry, foo.irb
来帮助解析器时不那么模糊)我从REPL中得到一个异常:
TypeError:没有将Array隐式转换为Hash
但是,如果我使用单例方法修改实例来捕获*hashes
参数的值,我会看到一个哈希数组,我可以合并得很好。考虑以下:
def foo.windows= *hashes
pp *hashes
end
foo.windows = foo.pry, foo.irb
#=> [{:pry=>"stuff pry\r"}, {:irb=>"stuff irb\r"}]
{}.merge *[{:pry=>"stuff pry\r"}, {:irb=>"stuff irb\r"}]
#=> {:pry=>"stuff pry\r", :irb=>"stuff irb\r"}
从#pp获取输出给了我一些按预期工作的东西。然而,当我深入挖掘时,事实证明有些东西正在层叠在Hash的额外嵌套上:
def foo.windows= *hashes
pp *hashes.inspect
end
foo.windows = foo.pry, foo.irb
"[[{:pry=>\"stuff pry\\r\"}, {:irb=>\"stuff irb\\r\"}]]"
即使返回值没有显示,也有一组额外的方括号导致数组嵌套。我真的不明白他们来自哪里。
因此,无论出于何种原因,我必须将数据展开,展平它,然后我就能合并:
def foo.windows= *hashes
@windows.merge! *hashes.flatten
end
# The method still returns unexpected results here, but...
foo.windows = foo.pry, foo.irb
#=> [{:pry=>"stuff pry\r"}, {:irb=>"stuff irb\r"}]
# the value stored in the instance variable is finally correct!
foo.windows
#=> {:pry=>"stuff pry\r", :irb=>"stuff irb\r"}
所以是的,我已经设法解决了这个问题。但是,我的问题是关于为什么合并散列不能按预期工作,以及额外的嵌套层来自何处。我不期望哈希数组数组,而是哈希数组。我的理解是否存在差距,或者这是某种奇怪的边缘情况?
更重要的是,为什么这么难以调试?我希望#pp或#inspect向我展示我真正拥有的对象,而不是在我清楚地拥有包含Hash的数组的数组时向我显示一个Hashes数组作为返回值。
你错过的是Ruby parser doesn't allow setter methods with more than one parameter。
当您将多个参数传递给setter时,它们会自动放在一个数组上(因为a = 1, 2
与a = [1, 2]
的含义相同):
def foo.a=(values)
pp values
end
foo.a = 1, 2 # [1, 2]
但是,如果定义了splat参数,因为该数组被认为是单个参数,所以会发生这种情况:
def foo.a=(*values)
pp values
end
foo.a = 1, 2 # [[1, 2]]
首先,*hashes
可能意味着两件截然不同的事情:
def windows=(*hashes)
的意思是“按照出现的顺序将传递给此方法的所有参数存储到数组hashes
中。”@windows.merge! *hashes
使用hashes
的项目作为方法调用merge!
的个别参数。但是,当你有一个赋值方法时,listing several values automatically creates an array:
您可以通过在分配时列出多个值来隐式创建数组:
a = 1, 2, 3 p a # prints [1, 2, 3]
因此foo.windows(foo.pry, foo.irb)
与
def windows(*hashes)
pp hashes
@windows.merge! *hashes
end
将按预期打印[{:pry=>"stuff pry\r"}, {:irb=>"stuff irb\r"}]
。但是由于你有一个赋值方法,你应该从你的定义中删除星号。
def windows=(hashes)
@windows.merge!(*hashes)
end