我可以使用默认值在 Ruby 中创建一个数组吗? [英] Can I create an array in Ruby with default values?
问题描述
Perl 对默认值非常友好:
Perl is pretty nice about default values:
: jmglov@laurana; perl -e '@foo; printf "%d\n", $foo[123]'
0
: jmglov@laurana; perl -e '%foo; printf "%d\n", $foo{bar}'
0
Ruby 可以做同样的事情,至少对于哈希:
Ruby can do the same, at least for hashes:
>> 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]
是否可以为数组提供默认值,因此当它们被自动扩展时,它们会被填充为 0 而不是 nil?
Is it possible to supply a default value for arrays, so when they are auto-extended, they're filled with 0 instead of 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.+
更新1:我的一位同事指出我可以使用#compact
来解决#inject
问题,而#to_i
解决标准 element-at-index 问题:
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:
>> 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]
更新 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]
更新 3:这开始看起来像打地鼠!
Update 3: this is starting to look like whack-a-mole!
>> foo
=> [nil, nil, nil, nil, 123]
>> foo[-2..-1]
TypeError: can't convert Range into Integer
但我们可以处理:
>> 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]
我现在不得不(害羞地)承认我将子类化 Array
以避免我的代码混乱:
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
感谢所有创造性的解决方案.确实是 TIMTOWTDI!
Thanks for all the creative solutions. TIMTOWTDI indeed!
推荐答案
鉴于 Ruby 为不存在的元素返回 nil
(相对于 index-out-of-bounds 类型错误),你可以只使用或":
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":
a = [1,2,3]
puts a[5] # => nil
puts a[5] || "a default" # => a default
您可以采用猴子补丁方法,但您可能不想在比 1 文件脚本更大的任何内容中执行此操作:
You could take the monkey patch approach, but you probably would not want to do this in anything larger than a 1-file script:
a = [1,2,3]
def a.[](index)
self.at(index) || "a default"
end
puts a[5] # => "a default"
这篇关于我可以使用默认值在 Ruby 中创建一个数组吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!