Perl est assez gentil avec les valeurs par défaut:
: jmglov@laurana; Perl -e '@foo; printf "%d\n", $foo[123]'
0
: jmglov@laurana; Perl -e '%foo; printf "%d\n", $foo{bar}'
0
Ruby peut faire la même chose, au moins pour les hashes:
>> foo = Hash.new(0)
=> {}
>> foo[:bar]
=> 0
Mais la même chose ne fonctionne apparemment pas pour les tableaux:
>> foo = Array.new(0)
=> []
>> foo[123]
=> nil
>> foo[124] = 0
=> 0
>> foo[456] = 0
=> 0
>> foo[455,456]
=> [nil, 0]
Est-il possible de fournir une valeur par défaut pour les tableaux, de sorte que lorsqu'ils sont auto-étendus, ils sont remplis avec 0 au lieu de zéro?
Bien sûr, je peux contourner cela, mais au prix de l'expressivité:
>> 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.+
Mise à jour 1: Un de mes collègues a souligné que je peux utiliser #compact
pour résoudre le problème #inject
et #to_i
pour résoudre le problème standard élément-à-index:
>> 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]
Mise à jour 2: Merci à Andrew Grimm pour une solution au problème +=
:
>> foo = []
=> []
>> def foo.[](i)
>> fetch(i) {0}
>> end
=> nil
>> foo[4]
=> 0
>> foo
=> []
>> foo[4] += 123
=> 123
>> foo
=> [nil, nil, nil, nil, 123]
Mise à jour 3: cela commence à ressembler à une taupe!
>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
TypeError: can't convert Range into Integer
Mais nous pouvons nous en occuper:
>> 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]
Je dois maintenant admettre (penaud) que je vais sous-classer Array
pour ne pas encombrer mon code:
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
Merci pour toutes les solutions créatives. TIMTOWTDI en effet!
Étant donné que Ruby renvoie nil
pour un élément non existant (par opposition à une erreur de type index-hors-des limites), vous pouvez simplement utiliser un "ou":
a = [1,2,3]
puts a[5] # => nil
puts a[5] || "a default" # => a default
Vous pouvez utiliser l'approche du patch de singe, mais vous ne voudriez probablement pas faire cela dans un script plus volumineux qu'un script à un fichier:
a = [1,2,3]
def a.[](index)
self.at(index) || "a default"
end
puts a[5] # => "a default"
Non étendu automatiquement, mais initialisé à la longueur spécifiée avec une valeur par défaut:
>> Array.new(123, 0)
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Si vous avez affaire à des entiers, vous pouvez appeler to_i
:
foo = []
foo[100]
#=> nil
foo[100].to_i
#=> 0
foo[100] = 3
foo[100]
#=> 3
UPD
Oh, je n'ai pas lu tout le sujet :)
afin que vous puissiez utiliser ceci:
foo.inject{|a,b| a.to_i + b.to_i }
qui, en fait, pas le plus intelligent
Je vais mettre la solution élégante de Johans là-bas: foo.compact.inject(:+)
Une autre approche serait de remplacer la méthode Array#[]
et de renvoyer la valeur par défaut s'il n'y a pas d'élément
class Array
def [](index)
self.at(index) ? self.at(index) : 0
end
end
et
arr = [1,2,3]
puts arr[0] # print 1
puts arr[5] # print 0
Je pense qu'un tableau est une abstraction erronée si vous souhaitez étendre automatiquement le tableau. Ajoutez un autre niveau d'abstraction.
Edit (de notre discussion): / L'important est que le code pour atteindre votre objectif se trouve au bon endroit (principe de responsabilité unique) et que cet endroit est pas votre "code client", d'où la nécessité d'une nouvelle classe. L'extension de la classe Array existante (via héritage/mixin) est probablement préférable à l'encapsulation du comportement souhaité dans une nouvelle classe.
Le moyen le plus simple serait:
new_array = Array.new(size, default_value)
Par exemple:
new_array = Array.new(5,"foo")