如何处理 Ruby 中自动激活哈希的组合 []+= ?

发布于 2024-09-07 18:12:11 字数 1946 浏览 3 评论 0原文

为了实现 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

这个类的更高级的定义Joshua提出,这对我来说有点难以理解。

问题

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

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

您将如何处理它?可以定义[]+=运算符吗?


相关问题

  1. Ruby 中是否可以像 PHP 中那样自动初始化多维哈希数组?
  2. 在 Ruby 中使用新运算符对自动激活哈希值进行多重初始化 ruby哈希初始化r
  3. 仍然开放: 如何在 Ruby 中创建用于深度复制/克隆对象的运算符?

In order to implement auto-vivification of Ruby hash, one can employ the following class

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

This class allows to do the following things

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

There is a bit more advanced definition of this class proposed by Joshua, which is a bit hard for me to understand.

Problem

There is one situation, where I think the new class can be improved. The following code fails with the error message NoMethodError: undefined method '+' for {}:AutoHash

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

What would you do to handle it? Can one define []+= operator?


Related questions

  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. still open: How to create an operator for deep copy/cloning of objects in Ruby?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

樱花坊 2024-09-14 18:12:11

ruby 中无法定义 []+= 方法。当您键入时会发生的情况

x[y] += z

是,

x[y] = x[y] + z

[][]= 方法都会在 x 上调用(以及 + > 在 x[y] 上调用,在本例中是一个 AutoHash)。我认为处理这个问题的最好方法是在 AutoHash 上定义一个 + 方法,它只会返回它的参数。这将使 AutoHash.new[:x] += y 适用于几乎任何类型的 y,因为 y.class< 的“空”版本/code> ('' 表示字符串,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

:)

There is no way to define a []+= method in ruby. What happens when you type

x[y] += z

is

x[y] = x[y] + z

so both the [] and []= methods are called on x (and + is called on x[y], which in this case is an AutoHash). I think that the best way to handle this problem would be to define a + method on AutoHash, which will just return it's argument. This will make AutoHash.new[:x] += y work for just about any type of y, because the "empty" version of y.class ('' for strings, 0 for numbers, ...) plus y will almost always equal y.

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

Adding that method will make both of these work:

# 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 "

And by the way, here is a cleaner version of your code:

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

:)

悲欢浪云 2024-09-14 18:12:11

我认为你想要的是这样的:

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

hash['foo'] += 3
<代码># => 3

这将返回 3,然后是 6,等等,不会出现错误,因为新值默认分配为 0。

What I think you want is this:

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

hash['foo'] += 3
# => 3

That will return 3, then 6, etc. without an error, because the the new value is default assigned 0.

节枝 2024-09-14 18:12:11
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 }
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 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文