Perl对于默认值非常好:

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

Ruby也可以做到这一点,至少对于哈希:

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

但同样的方法似乎不适用于数组:

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

是否可以为数组提供默认值,以便在自动扩展数组时,使用0而不是nil填充它们?

我当然可以解决这个问题,但要以表达能力为代价:

>> 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:我的一位同事指出,我可以用#compact来解决#inject问题,用#to_i来解决索引问题的标准元素:

>> 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:感谢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]

这看起来像是打鼹鼠!

>> 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:

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

感谢所有创造性的解决方案.真的!

推荐答案

假设Ruby对不存在的元素返回nil(与索引越界类型错误相反),您可以使用"or":

a = [1,2,3]
puts a[5]  # => nil
puts a[5] || "a default"  # => a default

您可以采用monkey patch方法,但您可能不希望在任何大于1文件脚本的情况下执行此操作:

a = [1,2,3]
def a.[](index)
  self.at(index) || "a default"
end
puts a[5]   # => "a default"

Ruby相关问答推荐

Ruby脚本递归地创建深度嵌套的目录

当Ruby `Complex` 类除了`==` 之外没有任何关系运算符时,它的祖先怎么能有`Comparable`?

有没有一种简单的方法可以在 Ruby 中获取图像尺寸?

RSpec 模拟对象示例

Rspec:应该是(this or that)

用于 ruby​​ gems 的新 10.9 OSX 的命令行工具?

Ruby 局部变量未定义

Ruby:如何计算相对于另一个路径的路径?

有效的Electron邮件地址正则表达式?

如何自定义 Jekyll 的 url?

铲子 (<<) 运算符如何在 Ruby 哈希中工作?

在 Ruby 中取消定义变量

Mustache Templates 可以做模板扩展吗?

我安装了一个 gem,但 require 'gemname' 不起作用.为什么?

Xcode - 配置:错误:在 $PATH 中找不到可接受的 C 编译器

Ruby - Array#<< 和 Array#push 之间的区别

Ruby中的urldecode?

什么是 Rakefile?

Python 中的一切都像 Ruby 一样是对象吗?

如何在没有 RVM 的 Ubuntu 上安装 Ruby 2