【问题标题】:How to handle combination []+= for auto-vivifying hash in Ruby?如何处理组合 []+= 在 Ruby 中自动激活哈希?
【发布时间】:2010-07-03 17:59:14
【问题描述】:

为了实现 Ruby 哈希的自动生存,可以使用以下类

class AutoHash < Hash
  def initialize(*args)
    super()
    @update, @update_index = args[0][:update], args[0][:update_key] unless 
args.empty?
  end

  def [](k)
    if self.has_key?k
      super(k)
    else
      AutoHash.new(:update => self, :update_key => k)
    end
  end

  def []=(k, v)
    @update[@update_index] = self if @update and @update_index
    super
  end

  def few(n=0)
    Array.new(n) { AutoHash.new }
  end
end

这个类允许做以下事情

a = AutoHash.new
a[:a][:b] = 1
p a[:c] # => {}             # key :c has not been created
p a     # => {:a=>{:b=>1}}  # note, that it does not have key :c

a,b,c = AutoHash.new.few 3
b[:d] = 1
p [a,b,c] # => [{}, {:d=>1}, {}]  # hashes are independent

这个类有a bit more advanced definitionproposed by Joshua,有点看不懂。

问题

有一种情况,我认为新职业可以改进。以下代码失败并显示错误消息NoMethodError: undefined method '+' for {}:AutoHash

a = AutoHash.new
5.times { a[:sum] += 10 }

你会怎么处理它?可以定义[]+= 运算符吗?


相关问题

  1. Is auto-initialization of multi-dimensional hash array possible in Ruby, as it is in PHP?
  2. Multiple initialization of auto-vivifying hashes using a new operator in Ruby ruby hash initialization r
  3. 仍然开放: How to create an operator for deep copy/cloning of objects in Ruby?

【问题讨论】:

  • 你想用最后一段代码做什么,你想发生什么?如果您希望a[:sum] 为50,则必须在5.times 行之前将其初始化为零,因为AutoHash 无法知道您要在其中存储数字。

标签: ruby hash operator-keyword autovivification


【解决方案1】:

没有办法在 ruby​​ 中定义 []+= 方法。键入时会发生什么

x[y] += z

x[y] = x[y] + z

所以[][]= 方法都在x 上调用(+x[y] 上调用,在这种情况下是AutoHash)。我认为处理这个问题的最好方法是在AutoHash 上定义一个+ 方法,它只会返回它的参数。这将使AutoHash.new[:x] += y 几乎适用于任何类型的y,因为y.class 的“空”版本('' 用于字符串,0 用于数字,...)加上y 几乎总是等于y

class AutoHash
  def +(x); x; end
end

添加该方法将使这两项工作:

# Numbers:
a = AutoHash.new
5.times { a[:sum] += 10 }
a[:sum] #=> 50

# Strings:
a = AutoHash.new
5.times { a[:sum] += 'a string ' }
a[:sum] #=> "a string a string a string a string a string "

顺便说一下,这是您的代码的更简洁版本:

class AutoHash < Hash
  def initialize(args={})
    super
    @update, @update_index = args[:update], args[:update_key]
  end

  def [](k)
    if has_key? k
      super(k)
    else
      AutoHash.new :update => self, :update_key => k
    end
  end

  def []=(k, v)
    @update[@update_index] = self if @update and @update_index
    super
  end

  def +(x); x; end

  def self.few(n)
    Array.new(n) { AutoHash.new }
  end
end

:)

【讨论】:

  • 酷!非常感谢,阿德里安!有一刻我在想a[:sum] 已经有一个不同类型的值的情况,但后来我意识到这样的异常引发是一件好事。
  • @Andrei:当a[:sum] 的值不是AutoHash 时,将引发异常。例如:a = AutoHash.new; a[:x] = 5; a[:x] += 'str' 将引发异常。
【解决方案2】:

我想你想要的是这样的:

hash = Hash.new { |h, k| h[k] = 0 }

hash['foo'] += 3 # =&gt; 3

这将返回 3,然后是 6,以此类推,没有错误,因为新值默认分配为 0。

【讨论】:

    【解决方案3】:
    require 'xkeys' # on rubygems.org
    
    a = {}.extend XKeys::Hash
    a[:a, :b] = 1
    p a[:c] # => nil (key :c has not been created)
    p a # => { :a => { :b => 1 } }
    
    a.clear
    5.times { a[:sum, :else => 0] += 10 }
    p a # => { :sum => 50 }
    

    【讨论】:

      猜你喜欢
      • 2010-12-02
      • 2014-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-14
      • 1970-01-01
      • 2021-07-25
      • 1970-01-01
      相关资源
      最近更新 更多