Perl is pretty nice about default values:
Perl对于默认值非常好:
: jmglov@laurana; perl -e '@foo; printf "%d\n", $foo[123]'
0
: jmglov@laurana; perl -e '%foo; printf "%d\n", $foo{bar}'
0
Ruby can do the same, at least for hashes:
Ruby也可以这样做,至少对于散列:
>> foo = Hash.new(0)
=> {}
>> foo[:bar]
=> 0
But the same seemingly does not work for arrays:
但同样的方法似乎不适用于数组:
>> foo = Array.new(0)
=> []
>> foo[123]
=> nil
>> foo[124] = 0
=> 0
>> foo[456] = 0
=> 0
>> foo[455,456]
=> [nil, 0]
Is it possible to supply a default value for arrays, so when they are auto-extended, they're filled with 0 instead of nil?
是否有可能为数组提供默认值,因此当它们自动扩展时,它们将被填充为0而不是nil?
Of course I can work around this, but at a cost to expressiveness:
当然,我可以解决这个问题,但付出的代价是:
>> 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: One of my colleagues pointed out that I can use #compact
to solve the #inject
issue, and #to_i
to solve the standard element-at-index issue:
更新1:我的一个同事指出我可以使用#compact来解决#inject问题,使用#to_i来解决标准的element-at-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]
Update 2: Thanks to Andrew Grimm for a solution to the +=
issue:
更新2:感谢Andrew Grimm对+=问题的解决方案:
>> 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: this is starting to look like whack-a-mole!
更新3:这看起来有点像打鼹鼠!
>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
TypeError: can't convert Range into Integer
But we can deal with that:
但我们可以解决这个问题:
>> 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]
I now have to admit (sheepishly) that I'll subclass Array
to avoid cluttering my 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
Thanks for all the creative solutions. TIMTOWTDI indeed!
谢谢你的创意解决方案。TIMTOWTDI确实!
6 个解决方案
#1
16
Given that Ruby returns nil
for a non-existing element (as opposed to index-out-of-bounds type error), you could just use an "or":
如果Ruby为不存在的元素返回nil(与索引越界类型错误相反),您可以使用“or”:
a = [1,2,3]
puts a[5] # => nil
puts a[5] || "a default" # => a default
You could take the monkey patch approach, but you probably would not want to do this in anything larger than a 1-file script:
您可以采用monkey patch方法,但是您可能不希望使用任何大于1个文件的脚本:
a = [1,2,3]
def a.[](index)
self.at(index) || "a default"
end
puts a[5] # => "a default"
#2
105
Not auto extended, but initialized to the specified length with a default value:
不是自动扩展,而是初始化为指定长度,默认值为:
>> 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]
#3
3
I'll put Johans elegant solution out there: foo.compact.inject(:+)
我将把Johans优雅的解决方案放在这里:foo.compact.inject(:+)
#4
3
If you're dealing with integers you can call to_i
:
如果要处理整数,可以调用to_i:
foo = []
foo[100]
#=> nil
foo[100].to_i
#=> 0
foo[100] = 3
foo[100]
#=> 3
UPD
乌利希期刊指南
Oh, I didn't read all topic :)
哦,我没读过所有的主题
so you can use this:
你可以用这个:
foo.inject{|a,b| a.to_i + b.to_i }
which, actually, not the smartest one
哪一个不是最聪明的
#5
2
Another approach would be overriding the Array#[]
method and return the default value if there is no item
另一种方法是覆盖数组#[]方法,如果没有项,则返回默认值。
class Array
def [](index)
self.at(index) ? self.at(index) : 0
end
end
and
和
arr = [1,2,3]
puts arr[0] # print 1
puts arr[5] # print 0
#6
1
I think an array is the wrong abstraction if you want to auto extend the array. Add another level of abstraction.
如果你想自动扩展数组,我认为数组是错误的抽象。添加另一个抽象级别。
Edit (from our discussion): The important thing is that the code to achieve your goal is located in the right place (single responsibility principle), and that place is not your "client code", hence the need for a new class. Extending the existing Array class (through inheritance/mixin) is probably better than encapsulating the wanted behaviour in an entierly new class.
编辑(从我们的讨论中):重要的是实现目标的代码位于正确的位置(单一职责原则),并且该位置不是您的“客户端代码”,因此需要一个新的类。扩展现有数组类(通过继承/mixin)可能比在纯新类中封装所需的行为要好。
#1
16
Given that Ruby returns nil
for a non-existing element (as opposed to index-out-of-bounds type error), you could just use an "or":
如果Ruby为不存在的元素返回nil(与索引越界类型错误相反),您可以使用“or”:
a = [1,2,3]
puts a[5] # => nil
puts a[5] || "a default" # => a default
You could take the monkey patch approach, but you probably would not want to do this in anything larger than a 1-file script:
您可以采用monkey patch方法,但是您可能不希望使用任何大于1个文件的脚本:
a = [1,2,3]
def a.[](index)
self.at(index) || "a default"
end
puts a[5] # => "a default"
#2
105
Not auto extended, but initialized to the specified length with a default value:
不是自动扩展,而是初始化为指定长度,默认值为:
>> 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]
#3
3
I'll put Johans elegant solution out there: foo.compact.inject(:+)
我将把Johans优雅的解决方案放在这里:foo.compact.inject(:+)
#4
3
If you're dealing with integers you can call to_i
:
如果要处理整数,可以调用to_i:
foo = []
foo[100]
#=> nil
foo[100].to_i
#=> 0
foo[100] = 3
foo[100]
#=> 3
UPD
乌利希期刊指南
Oh, I didn't read all topic :)
哦,我没读过所有的主题
so you can use this:
你可以用这个:
foo.inject{|a,b| a.to_i + b.to_i }
which, actually, not the smartest one
哪一个不是最聪明的
#5
2
Another approach would be overriding the Array#[]
method and return the default value if there is no item
另一种方法是覆盖数组#[]方法,如果没有项,则返回默认值。
class Array
def [](index)
self.at(index) ? self.at(index) : 0
end
end
and
和
arr = [1,2,3]
puts arr[0] # print 1
puts arr[5] # print 0
#6
1
I think an array is the wrong abstraction if you want to auto extend the array. Add another level of abstraction.
如果你想自动扩展数组,我认为数组是错误的抽象。添加另一个抽象级别。
Edit (from our discussion): The important thing is that the code to achieve your goal is located in the right place (single responsibility principle), and that place is not your "client code", hence the need for a new class. Extending the existing Array class (through inheritance/mixin) is probably better than encapsulating the wanted behaviour in an entierly new class.
编辑(从我们的讨论中):重要的是实现目标的代码位于正确的位置(单一职责原则),并且该位置不是您的“客户端代码”,因此需要一个新的类。扩展现有数组类(通过继承/mixin)可能比在纯新类中封装所需的行为要好。