什么是在Ruby中通过数组迭代的“正确”方式?

PHP,所有的疣,在这个数字是相当不错的。 数组和哈希之间没有区别(也许我很天真,但这似乎显然是正确的),并通过迭代你只是做

foreach (array/hash as $key => $value) 

在Ruby中有很多方法来做这样的事情:

 array.length.times do |i| end array.each array.each_index for i in array 

散列更有意义,因为我总是使用它

 hash.each do |key, value| 

为什么我不能这样做的数组? 如果我想记住只有一个方法,我想我可以使用each_index (因为它使索引和值都可用),但它是令人讨厌的,必须做array[index]而不是value


哦,对,我忘了array.each_with_index 。 但是,这个吸引因为它的|value, key|hash.each|key, value| ! 这不是疯了吗?

这将遍历所有元素:

 array = [1, 2, 3, 4, 5, 6] array.each { |x| puts x } 

打印:

 1 2 3 4 5 6 

这将遍历所有给你的值和索引的元素:

 array = ["A", "B", "C"] array.each_with_index {|val, index| puts "#{val} => #{index}" } 

打印:

 A => 0 B => 1 C => 2 

我不太清楚你的问题,你正在寻找哪一个。

我觉得没有一个正确的方法。 有很多不同的方法来迭代,每个都有自己的利基。

  • each都足够用于很多用途,因为我不经常关心索引。
  • each_ with _index行为就像Hash#each – 你得到的值和索引。
  • each_index – 只是索引。 我不经常使用这个。 相当于“长度”。
  • map是迭代的另一种方法,当你想把一个数组转换成另一个数组的时候很有用。
  • select是要select子集时使用的迭代器。
  • inject对于生成总计或产品或收集单个结果非常有用。

记住这个看起来好像很多,但是不用担心,你可以不知道所有这些。 但是当你开始学习和使用不同的方法时,你的代码将变得更清晰和更清晰,并且你将会掌握Ruby。

我不是说Array – > |value,index|Hash – > |key,value| (见霍勒斯·勒布(Horace Loeb)的评论),但我说的是有一个理智的方式来期待这种安排。

当我处理数组时,我关注数组中的元素(而不是索引,因为索引是暂时的)。 该方法是每个索引,即每个+索引,或|每个索引|或|value,index| 。 这也与被视为可选参数的索引一致,例如| value | 相当于| value,index = nil | 这与| value,index |一致。

当我处理哈希值时,我经常更关注的是键值而不是值,而且我通常是按照这个顺序处理键和值, key => valuehash[key] = value

如果你想要鸭子打字,那么要么像Brent Longborough所表明的那样明确地使用定义的方法,要么像maxhawkins所显示的那样使用隐式方法。

Ruby是关于适应程序员的语言,而不是适合语言的程序员。 这就是为什么有这么多的方法。 有太多的方法来思考一些事情。 在Ruby中,你select最接近的代码,其余的代码通常非常简洁。

对于原来的问题,“什么是在Ruby中通过数组遍历的”正确“方式?”,好吧,我认为核心方式(即没有强大的语法糖或面向对象的权力)是这样做的:

 for index in 0 ... array.size puts "array[#{index}] = #{array[index].inspect}" end 

但Ruby是关于强大的语法糖和面向对象的强大function,但无论如何,这是等价的哈希值,而且键可以是有序的或不可以的:

 for key in hash.keys.sort puts "hash[#{key.inspect}] = #{hash[key].inspect}" end 

所以,我的回答是,“在Ruby中通过一个数组迭代的”正确“方式取决于你(即程序员或编程团队)和项目。” 更好的Ruby程序员可以做出更好的select(哪种语法权力和哪种面向对象的方法)。 更好的Ruby程序员继续寻找更多的方法。


现在,我想问另外一个问题:“Ruby向后遍历范围的”正确“方式是什么?” (这个问题是我如何来到这个页面。)

(对于前锋)很高兴:

 (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而不是面向对象的方法(只是)。 我想反复迭代:

  • 不用创build数组(考虑0..1000000000)
  • 为任何范围工作(如string,不只是整数)
  • 不使用任何额外的面向对象的权力(即没有类修改)

我相信这是不可能的,没有定义pred方法,这意味着修改Range类来使用它。 如果你能做到这一点,请让我知道,否则确认不可能性将不胜感激,虽然这将是令人失望的。 也许Ruby 1.9解决了这个问题。

(感谢您的阅读时间。)

当你需要的时候使用each_with_index。

 ary.each_with_index { |val, idx| # ... 

其他的答案都很好,但我想指出一个其他的外围事物:数组是有序的,而哈希不在1.8。 (在Ruby 1.9中,哈希按键的插入顺序sorting)。因此,在1.9之前,以与数组相同的方式/序列迭代哈希,这总是有明确的顺序。 我不知道PHP关联数组的默认顺序是什么(显然我的谷歌fu不够强大,但是我不知道如何考虑常规PHP数组和PHP关联数组)在这个上下文中是“相同的”,因为关联数组的顺序似乎是未定义的。

因此,Ruby方式对我来说似乎更加清晰直观。 🙂

试图做与数组和哈希相同的事情可能只是一种代码味道,但是,如果你想要一致的行为,那么冒着被我昵称为一个蹩脚的半猴子补丁的风险,这样做会不会有诀窍?

 class Hash def each_pairwise self.each { | x, y | yield [x, y] } end end class Array def each_pairwise self.each_with_index { | x, y | yield [y, x] } end end ["a","b","c"].each_pairwise { |x,y| puts "#{x} => #{y}" } {"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y| puts "#{x} => #{y}" } 

我一直在试图build立一个菜单(在露营Markaby )使用哈希。

每个项目有2个元素:一个菜单标签和一个URL ,所以一个哈希看起来是正确的,但'Home'的'/'URL总是最后出现(如你所期望的一个哈希),所以菜单项出现在错误订购。

each_slice中使用数组可以完成这项工作:

 ['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link| li {a label, :href => link} end 

为每个菜单项添加额外的值(例如,像一个CSS ID名称)只是意味着增加切片值。 所以,就像一个散列,但由任意数量的项目组成的组。 完善。

所以这只是说谢谢你无意中暗示了一个解决scheme!

很明显,但值得一提的是:我build议检查数组的长度是否可以被slice值整除。

以下是您的问题中列出的四个选项,由控制自由安排。 你可能想要使用一个不同的取决于你所需要的。

  1. 简单地通过值:

     array.each 
  2. 简单地通过索引:

     array.each_index 
  3. 通过索引+索引variables:

     for i in array 
  4. 控制循环计数+索引variables:

     array.length.times do | i | 

如果你使用可枚举的 mixin(就像Rails所做的那样),你可以做类似于列表中的php代码片段。 只需使用each_slice方法并将散列平坦化即可。

 require 'enumerator' ['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" } # is equivalent to... {'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" } 

需要更less的猴子补丁。

但是,如果您有recursion数组或具有数组值的散列,则会造成问题。 在ruby 1.9中,这个问题是用flatten方法的一个参数来解决的,该方法指定了recursion的深度。

 # Ruby 1.8 [1,2,[1,2,3]].flatten => [1,2,1,2,3] # Ruby 1.9 [1,2,[1,2,3]].flatten(0) => [1,2,[1,2,3]] 

至于这是否是代码味,我不确定。 通常,当我必须向后弯腰迭代某些事情时,我会后退一步,意识到我正在攻击错误的问题。

正确的方法是你觉得最舒服的一个,哪个做你想做的。 在编程中,很less有一种“正确”的方式来做事情,更多的时候有多种方式可供select。

如果你对某种行事方式感到满意,那就做吧,除非行不通 – 那么现在是find更好的方法的时候了。

在Ruby 2.1中,each_with_index方法被删除。 相反,你可以使用each_index

例:

 a = [ "a", "b", "c" ] a.each_index {|x| print x, " -- " } 

生产:

 0 -- 1 -- 2 --