如何在Ruby中迭代一个特定步长的反向范围?

15

我可以像这样迭代

(0..10).step(2){|v| puts v}

因为反向范围等于空范围,所以我不能以这种方式迭代。

(10..0).step(2){|v| puts v}

这将对我毫无收益。当然,我可以像这样向后迭代。

10.downto(0){|v| puts v}

但是downto方法不允许我设置除了默认的1以外的其他步长。这是非常基本的事情,所以我想应该有一种内置的方式可以实现这个功能,但我不知道怎么做。


你可以像这样做:(0..10).step(2).reverse,但是如果从(10..0).step(2)中输出的结果不同,你可能需要添加一些逻辑。 - jvnill
(0..10).step(2).reverse 不是有效的代码;Ruby 报错:NoMethodError: undefined method 'reverse' for #<Enumerator: 0..10:step(2)> - the Tin Man
3个回答

23

为什么不使用Numeric#step

来自文档:

在每次调用时,使用从num开始以step(默认为1)递增的数字序列来调用块。当要传递给块的值大于limit(如果step为正)或小于limit(如果step为负)时,循环结束。如果所有参数都是整数,则循环使用整数计数器执行。如果任何参数是浮点数,则所有参数都将转换为浮点数,并且循环将执行floor(n + n*epsilon)+ 1次,其中n = (limit - num)/step。否则,循环从num开始,使用<或>运算符将计数器与limit进行比较,并使用+运算符自增。

irb(main):001:0> 10.step(0, -2) { |i| puts i }
10
8
6
4
2
0

3
不错。我还发现另一种方式,虽然不太优雅:(-10..0).step(2){|v| puts -1*v} - user1887348

4

通过跳过不需要的值来模拟 step 非常容易。像这样:

10.downto(0).each_with_index do |x, idx|
  next if idx % 3 != 0 # every third element
  puts x
end
# >> 10
# >> 7
# >> 4
# >> 1

如果你在each语句中有条件地跳过元素,那么你做错了,应该使用select:10.downto(0).select.with_index { |x, idx| idx % 3 == 0 }.each { |x| puts x } - SamStephens
@SamStephens:这是有争议的。 - Sergio Tulentsev
一切都是,但我会断言,如果你要使用 Ruby 的函数式集合特性,你应该始终如一地使用它们,而不是混合函数式和命令式风格。 - SamStephens
我个人并没有看到太多的函数式代码。 each 完全是命令式的。而 downto 生成器只是一种绕过性的解决方法。 - Sergio Tulentsev
2
我认为,一旦你习惯了使用函数式方法处理集合,我所呈现的跨越多行的格式更易读 - 通过将选择的过滤和每个元素的处理分开,你可以清晰地区分过滤和处理。 - SamStephens
1
@SamStephens:我明白你的意思。如果我们从外部获取每个主体(块参数或其他方式),那么分离过滤肯定是正确的方法。在这种情况下,它们同样好,我想说。还有性能问题。在没有延迟枚举器的情况下,我的解决方案可能会更快。(不过需要进行基准测试) - Sergio Tulentsev

0
10.step(0, -2) do |v| puts "#{v}" end




10
8
6
4
2
0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接