Как обработать комбинацию [] + = для автоживущего хэша в Ruby? - PullRequest
1 голос
/ 03 июля 2010

Для реализации авто-вивификации хэша 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

Существует немного более продвинутое определение этого класса , предложенное Джошуа , которое мне немного сложно понять.

Задача

В одной ситуации я думаю, что новый класс можно улучшить. Следующий код завершается ошибкой с сообщением об ошибке NoMethodError: undefined method '+' for {}:AutoHash

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

Что бы вы сделали, чтобы справиться с этим? Можно ли определить []+= оператор?


Похожие вопросы

  1. Возможна ли автоматическая инициализация многомерного хеш-массива в Ruby, как в PHP?
  2. Множественная инициализация авивививирующих хэшей с использованием нового оператора в Ruby инициализация хеша ruby ​​r
  3. все еще открыто: Как создать оператор для глубокого копирования / клонирования объектов в Ruby?

Ответы [ 3 ]

5 голосов
/ 03 июля 2010

Нет способа определить метод []+= в 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

:)

1 голос
/ 05 февраля 2015

Я думаю, что вы хотите это:

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

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

Это вернет 3, затем 6 и т. Д. Без ошибок, потому что новому значению по умолчанию присвоено 0.

0 голосов
/ 26 июля 2013
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 }
...