Ruby循环语句
Ruby中的循环语句
times方法循环
times是Integer类的一个方法,它后面跟一个语句块,如果没有给定语句块则返回一个迭代器对象。
1 | times {|i| block } → self |
当明确循环次数的时候,使用times方法进行循环将变得非常实用,代码可读性也非常强。
例如,要循环10次:
1 | 10.times { |
每次循环的时候,可以将次数作为语句块的变量使用(变量的值从0开始):
1 | 10.times do |i| |
for..in循环
这种for..in循环是迭代测试性循环,从一个容器中检测是否存在下一个元素,存在则迭代。for内部使用的是each方法,for是一个语法糖类型的语法式循环语句:
1 | for i in 1..5 |
for和while/until的关键字do可以替换为换行符或分号,例如下面是等价的:
1 | for i in 1..5 |
while和until循环
1 | # while循环 |
while和until作为修饰符的循环
将while和until作为修饰符,简化简单的循环代码:
1 | a = 0 |
如果while或until前面有多条语句,则可以使用begin…end语句块:
1 | a=0 |
只是需要注意的是,while和until作为修饰符的时候,当使用了begin...end
语句块后,循环体一定会执行一次,然后才开始第一次的条件判断。但如果不使用begin...end
,则不会出现这样的问题。
1 | x = 10 |
loop无限循环
loop方法是Kernel模块中提供的一个用来做无限循环的方法:
1 | loop { |
可以使用break来控制loop循环的退出。
each迭代循环
each方法用来迭代任何可迭代的对象,它的根在Enumerable模块,只要某个类实现了each方法用来yield元素,那么只要mix-in Enumerable之后就可以自动获得Enumerable中一大堆的each变体方法,如each_with_index
迭代方法。
1 | sum=0 |
break、next、redo
它们可以用在各种迭代、循环语句块中用来控制迭代、循环的流程。
- break退出当前层次的迭代结构、循环结构
- next立即进入下一轮迭代或循环(也可以认为是跳到当前循环的尾部从而进入下一轮循环)
- redo立即回到当前迭代/循环的头部,再次执行一次迭代/循环语句块
需要注意的是,break、next、redo除了可用于循环式的语句块,还可用于普通的语句块:
- break:等价于return,跳出当前语句块且跳出语句块所在函数
- next:跳过语句块,但不跳出函数
- redo:重新执行语句块
例如:
1 | def f |
几种循环的效率比较
只考虑速度不考虑内存的话。经过比较,while(until)是最快的。
1 | # while条件循环 |
Ruby中的循环优化
在循环次数多时,循环内部的代码优化对循环效率的提升是有益的。
1.循环中尽量不要重复创建重复的对象。
在Ruby中,数值、Symbol、true、false、nil都是不可变对象,循环中使用它们的常量形式其实每次都在引用同一个对象。
1 | $ ruby -e '5.times {puts 144444444423.__id__}' |
而Ruby中的字符串、数组、hash是可变对象,在循环中每次使用它们的常量形式都会重新创建新对象,这会使得效率降低。此时应将常量字符串、数组、hash定义成变量保存在循环的外部。
1 | $ ruby -e '5.times {puts "a".__id__}' |
此外,正则表达式的常量形式在循环中默认是不会重新编译的对象(除非使用了变量内插方式来构建常量的正则表达式对象,此时它是动态的,但可以使用『o』修饰符来强制缓存第一次的编译结果从而不再重新编译),所以循环中也可以使用正则的常量形式。但如果是Regexp类方法构建的正则表达式对象,则每次都会重新构建。
例如,下面两种循环,实现的需求是一样的,但是用了两种不同的方法:
1 | words.each {|w| next if w.include?("abc")} |
上面两个循环语句都是从一个words数组中匹配是否包含abc字符串。但是在第一个循环中,『abc』以常量的方式存在,每次迭代时都重新创建、销毁这个字符串对象,而第二个循环中只构建了一次正则表达式对象。所以,虽然include?()
的检测比正则表达式的匹配性能更好,但是结果却是第二个循环表现更佳,特别是循环次数多的时候。