Ruby Study 6 : Loops and Iterators

前面多次介绍到了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语法的转换.

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

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

上两种写法都是先判断条件, 然后执行程序块. 后一种是先执行程序块然后判断条件.

执行结果 :

execute zero or more times
execute one or more times
0

6. until loops

until 相当于while not , 也有两种写法. 如下 :

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的方法.

例如 :

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 方法. 按照字符长度来选择最大最小值.

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

时间: 2024-09-15 08:08:35

Ruby Study 6 : Loops and Iterators的相关文章

Ruby Study 11 : Blocks, Procs, and Lambdas

Block在前面的讲解中已经多次用到, 这个章节来详细讲解一下. 需要注意的是Block在1.8和1.9中有一些特性是截然不同的, 所以需要特别注意. 尽量避免发生这样的情况, 否则你的代码在1.8可以跑, 1.9就有可能出问题了 1. What is a Block? 看个例子 : 3.times do |i| puts( i ) end 3.times { |i| puts( i ) } 蓝底的部分就是Block. 即do end,和{}的部分. Block由声明部分和代码部分组成, 声明部

Ruby Study 10 : Exception Handling

exception 在程序开发中是不可或缺的部分, 程序在使用过程中总会遇到不可预知的问题, 例如可预知的访问文件的程序可能在访问过程中文件被删除或移动等. 但是总会有不可预知的, 不可能在写程序时全部规避掉. 所以就有了exception handling. 在Ruby中exception 是Exception calss或者它的subclass的object. 下面来详细的讲解一下异常时如何捕获exception. 注意Ruby的Exception Handling分为几个层面 , 异常执行

Ruby Study 3 : String and Range class

1. Normal String Delimiter ( ' and " ) 前面两篇已经接触了很多String的例子, 下面再举一个, 注意看单引号和双引号的区别 : s1 = 'Hello' s2 = 'World' s3 = '#{s1} #{s2}' s4 = "#{s1} #{s2}" puts(s3) puts(s4) 执行结果 : #{s1} #{s2} Hello World 2. User Defined String Delimiter 当String中有

Ruby Study 8 : Methods

1. Class Methods 调用一个方法时, 可以通过定义这个方法的类的对象来调用, 那么这个称为实例方法. 还可以定义类方法, 这种方法的调用需要通过类来调用, 不能通过这个类的对象来调用. 其实我们创建的Class也是一个对象, 这个后面会讲到, 所以方法都是由对象来调用的. class MyClass def MyClass.classMethod # 定义class method可以使用这种写法, 后面还会介绍其他写法.class method也称为这个class的singleto

Ruby Study 12 : Symbols

本章主要讲解Symbol 类. 讲解Symbol比较重要的是大家需要了解object_id的知识. Symbol和Fixnum很相似, 同样的symbol它们的object_id是一致的, 而字符串则不是这样的, 举个例子来回忆一下: v1 = :name p v1 p v1.object_id p :name.object_id v2 = "name" p v2 p v2.object_id p "name".object_id v3 = 10 p v3 p v3

Ruby Study 7 : Conditional Statements

这篇的内容很简单, 基本上都是以前用过的语法, 稍微讲的详细一点. 1. if .. then .. else #!/opt/ruby/bin/ruby def get_daytype(aDay) if (aDay.to_s.downcase == 'saturday') || (aDay.to_s.downcase == 'sunday') # 分行写的话then可以忽略. daytype = 'weekend' else daytype = 'weekday' end return dayt

Ruby Study 4 : Array

Array class 1. introduce Ruby的Array类和其他的不太一样, Ruby中允许一个Array对象中存储不同类型的元素. 例如 : class Myclass end my1 = Myclass.new a1 = Array.new([1,"Digoal",[1,2,3,4,5],4,my1]) p a1 执行结果 :  [1, "Digoal", [1, 2, 3, 4, 5], 4, #<Myclass:0x1e11250>

Ruby Study 9 : Passing Arguments and Returning Values

1. Summarizing Instance, Class and Singleton methods Instance methods指的是可以被它所在的类的实例调用的方法. Class 方法和Singleton 方法指属于某个单独的对象的方法,不能被其他对象调用.(注意我把类方法也这么解释, 是因为类(包括BasicObject,Object,Module,Class以及自定义的类等)其实都是Class的对象.) 2. Returning Values 在很多编程语言中一般分有返回和不返回

Ruby Study 13 : Modules and Mixins

在Ruby中class的superclass只能有一个, childclass可以有多个. 所以一个class如果要继承多个class的特性是不可能的. 如果有这种需求, 可以考虑把这些特性封装在Module中. Module可以很好的解决这类问题, 通过require或者load或者include可以把Module的instance method等带入到当前环境, 也就是可以使用Module中的方法等. 一个class可包含多个Module, 接下来详细的讲解. 1. A Module Is