前面多次介绍到了Array, Hash, Set, Range, Matrix, Vector 等collection class的Iterator特性, 如each方法.
这次专门讲一讲Loops和Iterators.
1. for loops
for循环和其他编程语言类似, 看个例子 :
下面的例子中包含了,String, Array, Hash, Set, Range, Vector, Matrix的Iterator 操作以及each方法和for语法的转换.
执行结果 :
2. Blocks and Block Parameters
require "Matrix"
require "Set"
s1 = "abcd"
a1 = [1,2,3,4]
h1 = {:a=>1, :b=>2, :c=>3, :d=>4}
set1 = Set.new([1,2,3,4,1,2,3,4]) # 因为, Set class不保护重复数据, 所以就是1,2,3,4 .这里再提醒一下大家.
r1 = (1..4)
v1 = Vector[1,2,3,4]
v2 = Vector[5,6,7,8]
m1 = Matrix.columns([v1,v2])
m2 = Matrix.rows([v1,v2])
# each example
puts("each example\ns1:")
(0..s1.length-1).each {
|x|
print(s1["#{x}".to_i])
}
puts("\na1:")
a1.each {
|x|
print(x)
}
puts("\nh1:")
h1.each {
|x|
print(x)
}
puts("\nset1:")
set1.each {
|x|
print(x)
}
puts("\nr1:")
r1.each {
|x|
print(x)
}
puts("\nv1:")
v1.each {
|x|
print(x)
}
puts("\nv2:")
v2.each {
|x|
print(x)
}
puts("\nm1:")
m1.each {
|x|
print(x)
}
puts("\nm2:")
m2.each {
|x|
print(x)
}
# for example
puts("\nfor example\ns1:")
for x in (0..s1.length-1) do
print(s1["#{x}".to_i])
end
puts("\na1:")
for x in a1 do
print(x)
end
puts("\nh1:")
for x in h1 do
print(x)
end
puts("\nset1:")
for x in set1 do
print(x)
end
puts("\nr1:")
for x in r1 do
print(x)
end
puts("\nv1:")
for x in v1 do
print(x)
end
puts("\nv2:")
for x in v2 do
print(x)
end
puts("\nm1:")
for x in m1 do
print(x)
end
puts("\nm2:")
for x in m2 do
print(x)
end
执行结果 :
each example
s1:
abcd
a1:
1234
h1:
[:a, 1][:b, 2][:c, 3][:d, 4]
set1:
1234
r1:
1234
v1:
1234
v2:
5678
m1:
15263748
m2:
12345678
for example
s1:
abcd
a1:
1234
h1:
[:a, 1][:b, 2][:c, 3][:d, 4]
set1:
1234
r1:
1234
v1:
1234
v2:
5678
m1:
15263748
m2:
12345678
2. Blocks and Block Parameters
在Ruby中Iterator执行的那串代码被称为Block, 例如 :
(1..4).each do |x|
puts(x)
end
以下称为一个Block, x为Block parameter
|x|
puts(x)
执行一个Block类似执行一个函数, Block parameters类似函数的形参(s). block parameters可以由collection class在调用iterator类方法时传递给block. 例如each方法.
下面的例子是传递多个参数的例子 :
ms1 = [[1,2,3,4],5,[6,7]]
ms1.each {
|a,b,c,d|
p(a,b,c,d,"\n")
}
执行结果 :
第一个元素传递了4个参数,第二个元素传递了一个参数, 所以剩余的3个为nil.最后一个元素传递了2个参数, 其余2个为nil
3. Iterating upto and downto
1
2
3
4
"\n"
5
nil
nil
nil
"\n"
6
7
nil
nil
"\n"
3. Iterating upto and downto
除了each方法, 还有upto, downto方法可以与block结合使用.
Fixnum的upto和downto方法的例子如下 :
执行结果 :
0.upto(5) {
|x|
print(x)
}
print("\n")
5.downto(0) {
|x|
print(x)
}
执行结果 :
012345
543210
4. Multiple Iterator Arguments
多参数的传递上面已经举例说明过了, 如果传递的参数不够BLOCK想要的个数, 那么将有些block parameter 会为nil.
5. while loops
while loop类似其他编程语言, 只是有两种写法, 其中一种先判断条件然后执行, 另一种是先执行任何判断条件. 如下 :
puts("execute zero or more times")
i1 = 0
while i1<0
puts( i1 )
i1 += 1
end
i3 = 0
puts( i3 ) while i3<0
puts("execute one or more times")
i2 = 0
begin
puts( i2 )
i2 += 1
end while i2<0
上两种写法都是先判断条件, 然后执行程序块. 后一种是先执行程序块然后判断条件.
执行结果 :
6. until loops
execute zero or more times
execute one or more times
0
6. until loops
until 相当于while not , 也有两种写法. 如下 :
执行结果 :
7. loop
puts("execute zero or more times")
i1 = 0
until i1>=0
puts( i1 )
i1 += 1
end
i3 = 0
puts( i3 ) until i3>=0
puts("execute one or more times")
i2 = 0
begin
puts( i2 )
i2 += 1
end until i2>=0
执行结果 :
execute zero or more times
execute one or more times
0
7. loop
前面讲的for, while, until 都是有限制条件来跳出循环的, 接下来的循环代码(loop)则必须使用break来跳出循环.
并且loop可以使用大括号来指定执行代码也可以用do end. 而for, while, until只能用do end.
执行结果 :
arr = %W[d i g o a l]
i = 0
loop do
print(arr[i])
i += 1
if (i == arr.length) then
break
end
end
puts("\nanother example\n")
i = 0
loop {
print(arr[i])
i += 1
if (i == arr.length) then
break
end
}
执行结果 :
digoal
another example
digoal
8. Enumerable Module
前面所举了很多include?, min, max, collect等方法, 这些方法来自Enumerable Module , 很多class包含了这个Module, 例如Set, Hash, Range, Array等.
include? 方法用于判断某元素是否存在,
min, max很好理解.
collect 用于返回一个结构体.
同时, 这些方法有些可以传递块参数进去, 来看看.
执行结果 :
a1 = ["abc", "bc" , "efg", "google", "digoal"]
p a1.min
p a1.min {
|a,b|
a.length <=> b.length
}
p a1.collect {
|x|
x + "_new"
}
p a1.include?("abc")
执行结果 :
"abc"
"bc"
["abc_new", "bc_new", "efg_new", "google_new", "digoal_new"]
true
9. custom Comparisons
这个已经多次举例了,
这里再举一个更深入的, 使用Array存储一组字符串, 按照Array的index来进行排序.
执行结果 :
a1 = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
h1 = {
"monday" => 1,
"tuesday" => 2,
"wednesday" => 3,
"thursday" => 4,
"friday" => 5,
"saturday" => 6,
"sunday" => 7
}
p h1.sort
p h1.sort {
|x,y|
a1.index(x[0].downcase) <=> a1.index(y[0].downcase)
}
执行结果 :
[["friday", 5], ["monday", 1], ["saturday", 6], ["sunday", 7], ["thursday", 4], ["tuesday", 2], ["wednesday", 3]]
[["monday", 1], ["tuesday", 2], ["wednesday", 3], ["thursday", 4], ["friday", 5], ["saturday", 6], ["sunday", 7]]
10. each and yield
使用Ruby自带的类如前面所讲的Array, Set, Range, Hash等include了Enumerable Module. 可以使用它定义的方法如min, max, include?等.
接下来我们看看自定义的方法如何来使用这些Enumerable Module的方法(min, max, include?等)
首先看看Enumerable Module 定了些啥方法.
接下来的例子定义了一个包含Enumerable模块的class, 和一个不包含Enumerable的class, 通过他们的对象的methods方法相减得到的是Enumerable 模块新带来的方法, 我们可以从结果上看到除了min , max , collect还有熟悉的sort等方法.
输出结果 :
class Myclass1
include Enumerable
attr_accessor(:a)
def initialize (x)
@a = x
end
end
class Myclass2
end
mobj1 = Myclass1.new([1,2,3,4])
mobj2 = Myclass2.new()
print((mobj1.methods - mobj2.methods).sort)
输出结果 :
[:a, :a=, :all?, :any?, :chunk, :collect, :collect_concat, :count, :cycle, :detect, :drop, :drop_while, :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, :entries, :find, :find_all, :find_index, :first, :flat_map, :grep, :group_by, :include?, :inject, :map, :max, :max_by, :member?, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :partition, :reduce, :reject, :reverse_each, :select, :slice_before, :sort, :sort_by, :take, :take_while, :to_a, :zip]
接下来看看如何利用Enumerable带来的方法, 直接使用是不行的, 必须在自定义的类中新增一个each方法, 并且要在each方法中用yield方法返回值.
这些个yield返回的值将作为BLOCK parameter传递给这些Enumerable的方法.
例如 :
这里由于没有在Myclass1中定义each方法, 所以调用Enumerable带来的sort方法时会报错.
接下来定义each方法后, 再调用min,max,sort就不会报错了.
结果 :
接下来看看yield中的返回值修改一下会怎么样,
返回结果 :
class Myclass1
include Enumerable
attr_accessor(:a)
def initialize (x)
@a = x
end
end
mobj1 = Myclass1.new(["digoal","DIGOAL","abc","baidu","google"])
p mobj1.sort
这里由于没有在Myclass1中定义each方法, 所以调用Enumerable带来的sort方法时会报错.
C:/Users/digoal/Desktop/new.rb:10:in `sort': undefined method `each' for #<Myclass1:0x1e9fa98> (NoMethodError)
from C:/Users/digoal/Desktop/new.rb:10:in `<main>'
接下来定义each方法后, 再调用min,max,sort就不会报错了.
class Myclass1
include Enumerable
attr_accessor(:a)
def initialize (x)
@a = x
end
def each
@a.each {
|x|
yield(x)
}
end
end
mobj1 = Myclass1.new(["digoal","DIGOAL","abc","baidu","google"])
p mobj1.sort
p mobj1.min
p mobj1.max
结果 :
["DIGOAL", "abc", "baidu", "digoal", "google"]
"DIGOAL"
"google"
接下来看看yield中的返回值修改一下会怎么样,
class Myclass1
include Enumerable
attr_accessor(:a)
def initialize (x)
@a = x
end
def each
@a.each {
|x|
yield(x.downcase)
}
end
end
mobj1 = Myclass1.new(["digoal","DIGOAL","abc","baidu","google"])
p mobj1.sort
p mobj1.min
p mobj1.max
返回结果 :
["abc", "baidu", "digoal", "digoal", "google"]
"abc"
"google"
没错, Enumerable模块的这些方法,min,max,sort调用了each, 使用了yield返回的值. 所以在调用sort后会发现返回的都是小写.
接下来我们要举例的是复写Enumerable模块的min , max 方法. 按照字符长度来选择最大最小值.
执行结果 :
这种自定义包含Enumerable模块的类的对象, 同样也可以在这些方法后传入块. 例如 :
执行结果 :
【参考】
class Myclass1
include Enumerable
attr_accessor(:a)
def initialize (x)
@a = x
end
def each
@a.each {
|x|
yield(x)
}
end
def min
@a.min {
|x,y|
x.length <=> y.length
}
end
def max
@a.max {
|x,y|
x.length <=> y.length
}
end
def sort
@a.sort {
|x,y|
x.length <=> y.length
}
end
end
mobj1 = Myclass1.new(["digoal","DIGOAL","abc","baidu","google"])
p mobj1.sort
p mobj1.min
p mobj1.max
执行结果 :
["abc", "baidu", "digoal", "DIGOAL", "google"]
"abc"
"digoal"
这种自定义包含Enumerable模块的类的对象, 同样也可以在这些方法后传入块. 例如 :
class Myclass1
include Enumerable
attr_accessor(:a)
def initialize (x)
@a = x
end
def each
@a.each {
|x|
yield(x)
}
end
end
mobj1 = Myclass1.new(["digoal","DIGOAL1","abc","baidu","goooogle"])
p mobj1.sort {
|x,y|
x.to_s.length <=> y.to_s.length
}
执行结果 :
["abc", "baidu", "digoal", "DIGOAL1", "goooogle"]
【参考】
The Book Of Ruby
Ruby 1.9.3 API