我并不是说Array
->|value,index|
和Hash
->|key,value|
并不疯狂(请参阅 Horace Loeb 的评论),但我是说有一种理智的方式可以期待这种安排。
当我处理数组时,我专注于数组中的元素(不是索引,因为索引是暂时的)。方法是each 带索引,即each+index,或|each,index|,或|value,index|
。这也与被视为可选参数的索引一致,例如 |value| 等价于 |value,index=nil| 这与 |value,index| 一致。
当我处理哈希时,我通常更关注键而不是值,并且我通常按顺序处理键和值,要么key => value
要么hash[key] = value
。
如果您想要鸭式打字,那么要么显式使用 Brent Longborough 所示的定义方法,要么使用 maxhawkins 所示的隐式方法。
Ruby 就是让语言适应程序员,而不是程序员适应语言。这就是为什么有这么多方法。有很多方法可以思考某事。在 Ruby 中,您选择最接近的代码,其余代码通常非常简洁明了。
至于最初的问题,“在 Ruby 中迭代数组的“正确”方式是什么?”,好吧,我认为核心方式(即没有强大的语法糖或面向对象的能力)是:
for index in 0 ... array.size
puts "array[#{index}] = #{array[index].inspect}"
end
但是 Ruby 是关于强大的语法糖和面向对象的能力,但无论如何这里是哈希的等价物,键可以排序或不排序:
for key in hash.keys.sort
puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end
所以,我的回答是,“在 Ruby 中迭代数组的“正确”方法取决于您(即程序员或编程团队)和项目。”。更好的 Ruby 程序员会做出更好的选择(选择哪种语法能力和/或哪种面向对象的方法)。更好的 Ruby 程序员继续寻找更多的方法。
现在,我想问另一个问题,“在 Ruby 中反向迭代 Range 的“正确”方法是什么?”!(这个问题是我如何来到这个页面的。)
这样做很好(对于前锋):
(1..10).each{|i| puts "i=#{i}" }
但我不喜欢这样做(向后):
(1..10).to_a.reverse.each{|i| puts "i=#{i}" }
好吧,我实际上并不介意这样做太多,但是当我在倒退教学时,我想向我的学生展示一个很好的对称性(即差异最小,例如只添加反向或步骤 -1,但没有修改其他任何东西)。你可以做(对称):
(a=*1..10).each{|i| puts "i=#{i}" }
和
(a=*1..10).reverse.each{|i| puts "i=#{i}" }
我不太喜欢,但你做不到
(*1..10).each{|i| puts "i=#{i}" }
(*1..10).reverse.each{|i| puts "i=#{i}" }
#
(1..10).step(1){|i| puts "i=#{i}" }
(1..10).step(-1){|i| puts "i=#{i}" }
#
(1..10).each{|i| puts "i=#{i}" }
(10..1).each{|i| puts "i=#{i}" } # I don't want this though. It's dangerous
你最终可以做到
class Range
def each_reverse(&block)
self.to_a.reverse.each(&block)
end
end
但我想教授纯 Ruby 而不是面向对象的方法(目前还没有)。我想向后迭代:
- 不创建数组(考虑 0..1000000000)
- 适用于任何范围(例如字符串,而不仅仅是整数)
- 不使用任何额外的面向对象的能力(即没有类修改)
我相信如果不定义方法这是不可能的pred
,这意味着修改 Range 类以使用它。如果你能做到这一点,请告诉我,否则将不胜感激确认不可能,尽管它会令人失望。也许 Ruby 1.9 解决了这个问题。
(感谢您花时间阅读本文。)