Posso criar uma matriz em Ruby com valores padrã

Perl é bastante gentil com os valores padrão:

: jmglov@laurana; perl -e '@foo; printf "%d\n", $foo[123]'
0
: jmglov@lau,rana; perl -e '%foo; printf "%d\n", $foo{bar}'
0

Ruby pode fazer o mesmo, pelo menos para hashes:

>> foo = Hash.new(0)
=> {}
>> foo[:bar]
=> 0

Mas o mesmo aparentemente não funciona para matrizes:

>> foo = Array.new(0)
=> []
>> foo[123]
=> nil
>> foo[124] = 0
=> 0
>> foo[456] = 0
=> 0
>> foo[455,456]
=> [nil, 0]

É possível fornecer um valor padrão para matrizes, portanto, quando são estendidas automaticamente, são preenchidas com 0 em vez de nul

Claro que posso resolver isso, mas com um custo de expressividade:

>> foo[457,458] = 890, 321
=> [890, 321]
>> foo[456] += 789
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.+
>> foo.inject(0) {|sum, i| sum += (i || 0) }
=> 1211
>> foo.inject(:+)
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.+

Update 1: Um dos meus colegas apontou que eu posso usar#compact para resolver o#inject questão e#to_i para resolver o problema padrão do elemento no índice:

>> foo.include? nil
=> true
>> foo.compact.inject(:+)
=> 1211
>> foo[456,457]
=> [0, 890, 321]
>> foo[455..457]
=> [nil, 0, 890]
>> foo[455..457].map(&:to_i)
=> [0, 0, 890]

Update 2: Graças aAndrew Grimm para uma solução para o+= questão

>> foo = []
=> []
>> def foo.[](i)
>>   fetch(i) {0}
>> end
=> nil
>> foo[4]
=> 0
>> foo
=> []
>> foo[4] += 123
=> 123
>> foo
=> [nil, nil, nil, nil, 123]

Update 3: isso está começando a parecer uma sacudid

>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
TypeError: can't convert Range into Integer

Mas podemos lidar com isso:

>> def foo.[](index)
>>   if index.is_a? Range
>>     index.map {|i| self[i] }
>>   else
?>     fetch(index) { 0 }  # default to 0 if no element at index; will not cause auto-extension of array
>>   end
>> end
=> nil
>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
=> [nil, 123]

Agora eu tenho que admitir (timidamente) que vou subclasseArray para evitar bagunçar meu código:

class MyClass
  class ArrayWithDefault < Array
    def [](index)
      if index.is_a? Range
        index.map {|i| self[i] }
      else
        fetch(index) { 0 }  # default to 0 if no element at index; will not cause auto-extension of array
      end
    end
  end
end

Obrigado por todas as soluções criativas. TIMTOWTDI de fato!

questionAnswers(7)

yourAnswerToTheQuestion