¿Puedo crear una matriz en Ruby con valores predeterminados?
Perl es bastante amable con los valores predeterminados:
: jmglov@laurana; perl -e '@foo; printf "%d\n", $foo[123]'
0
: jmglov@lau,rana; perl -e '%foo; printf "%d\n", $foo{bar}'
0
Ruby puede hacer lo mismo, al menos para los hashes:
>> foo = Hash.new(0)
=> {}
>> foo[:bar]
=> 0
Pero lo mismo aparentemente no funciona para las matrices:
>> foo = Array.new(0)
=> []
>> foo[123]
=> nil
>> foo[124] = 0
=> 0
>> foo[456] = 0
=> 0
>> foo[455,456]
=> [nil, 0]
¿Es posible proporcionar un valor predeterminado para las matrices, por lo que cuando se extienden automáticamente, se rellenan con 0 en lugar de cero?
Por supuesto que puedo solucionar esto, pero a costa de la expresividad:
>> 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.+
Actualización 1: Uno de mis colegas señaló que puedo usar#compact
para resolver el#inject
problema y#to_i
para resolver el problema estándar del elemento en el í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]
Actualización 2: Gracias aAndrew Grimm para una solución a la+=
problema
>> foo = []
=> []
>> def foo.[](i)
>> fetch(i) {0}
>> end
=> nil
>> foo[4]
=> 0
>> foo
=> []
>> foo[4] += 123
=> 123
>> foo
=> [nil, nil, nil, nil, 123]
Actualización 3: ¡Esto está empezando a parecerse a Whack-a-mole!
>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
TypeError: can't convert Range into Integer
Pero podemos lidiar con eso:
>> 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]
Ahora tengo que admitir (tímidamente) que subclasificaréArray
para evitar saturar mi 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
Gracias por todas las soluciones creativas. TIMTOWTDI de hecho!