如何分配Ruby var =返回值,而不是传入的值?

时间:2013-08-12 01:50:23

标签: ruby

添加到存储在哈希表中的列表有一个很好的习惯用语:

(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]

还有更优雅的方法吗?

3 个答案:

答案 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} })。

通过使用带有块(doch[:a] += [10]的构造函数来解决它。每次创建新密钥时,都会调用块,并且每个值都是不同的数组。

编辑:修正了@Uri Agassi正在撰写的错误。