添加到存储在哈希表中的列表有一个很好的习惯用语:
(hash[key] ||= []) << new_value
现在,假设我编写了一个派生哈希类,就像在Hashie中找到的那样,它对我存储的任何哈希进行深度转换。然后我存储的内容不会是我传递给=运算符的同一个对象;散列可以转换为Mash或Clash,并且可以复制数组。
这是问题所在。 Ruby显然从var =方法返回传入的值,而不是存储的值。 var =方法返回什么并不重要。下面的代码说明了这一点:
class C
attr_reader :foo
def foo=(value)
@foo = (value.is_a? Array) ? (value.clone) : value
end
end
c=C.new
puts "assignment: #{(c.foo ||= []) << 5}"
puts "c.foo is #{c.foo}"
puts "assignment: #{(c.foo ||= []) << 6}"
puts "c.foo is #{c.foo}"
输出
assignment: [5]
c.foo is []
assignment: [6]
c.foo is [6]
当我把这个错误发布给Hashie时,Danielle Sucher解释了发生了什么,并指出“foo.send:bar =,1”返回bar =方法返回的值。 (研究的帽子提示!)所以我想我能做到:
c=C.new
puts "clunky assignment: #{(c.foo || c.send(:foo=, [])) << 5}"
puts "c.foo is #{c.foo}"
puts "assignment: #{(c.foo || c.send(:foo=, [])) << 6}"
puts "c.foo is #{c.foo}"
打印
clunky assignment: [5]
c.foo is [5]
assignment: [5, 6]
c.foo is [5, 6]
还有更优雅的方法吗?
答案 0 :(得分:6)
分配评估为分配的值。周期。
在其他一些语言中,作业是陈述,因此它们不会评估任何内容。这些只是两个明智的选择。要么不评估任何内容,要么评估所分配的值。其他一切都太令人惊讶了。
由于Ruby没有语句,因此实际上只有一种选择。
唯一的“解决方法”是:不要使用赋值。
答案 1 :(得分:5)
c.foo ||= []
c.foo << 5
使用两行代码并不是世界末日,而且眼睛也更容易。
答案 2 :(得分:1)
最漂亮的方法是使用hash的默认值:
# h = Hash.new { [] }
h = Hash.new { |h,k| h[k] = [] }
但是,由于Ruby存储变量的方式,你不能使用Hash.new([])
然后使用<<
:
h = Hash.new([])
h[:a] # => []
h[:b] # => []
h[:a] << 10
h[:b] # => [10] O.o
它是由Ruby存储变量引用引起的,因此我们只创建了一个数组实例,ad将其设置为默认值,然后它将在所有哈希单元之间共享(除非它将被覆盖,即{{1} })。
通过使用带有块(doc)h[:a] += [10]
的构造函数来解决它。每次创建新密钥时,都会调用块,并且每个值都是不同的数组。
编辑:修正了@Uri Agassi正在撰写的错误。