Showing posts with label 条件控制语句. Show all posts
Showing posts with label 条件控制语句. Show all posts

Thursday, October 23, 2008

(9)BEGIN和END

BEGIN和END是Ruby中的关键字,用于定义一段代码。这两个关键字后需要跟一对{}代码就放在其中。记住这里的BEGIN和END 与小写的begin和end是不同的。BEGIN中的代码总是会被执行一次,不管这段代码在什么位置,即使想下面的例子中这段代码在一个不可能执行的条件分支中,它还是会被执行一次。而END中的代码是在所有的代码都执行之后才会被执行。而且,如果这两个关键字中的代码存在于循环或者迭代器中时,他们只会被执行一次。
if (false) 
  BEGIN {
    puts "if";                   # 这里会被打印
    a = 4;                       # 这里的变量只存在于BEGIN块中
  }
else
  BEGIN { puts "else" }          # 这里也会被打印
end

10.times {BEGIN { puts "loop" }} # BEGIN位于迭代器中时,只被打印一次

如你所见,在BEGIN中的变量只在它所在的BEGIN块中又要,而END不同,由于它在所有代码执行之后执行,所以,外部变量也可以在END中访问。
a = 4;
if (true) 
  END {                        # 这里会执行
    puts "if";                 # 打印
    puts a                     # 这里的a是一个外部变量,打印 "4"
  }
else
  END { puts "else" }          # 这里不会执行,因为前面的条件判断不成立
end
10.times {END { puts "loop" }} # 在迭代器中只执行一次

(8)异常与异常的处理

程序中会出现异常,或者说错误,比如除法的除数为0,访问一个不存在的文件或者调用一个不存在的方法,又或者访问的网络突然中断,这些都会引起一场,一般情况下异常会造成程序的中断,但是我们也可以通过处理这些异常让程序按照我们的想法继续执行。Ruby中通过raise和rescue还处理异常,raise用于抛出异常,resuce用于捕获异常。所有的异常都继承自Exception类,下列给出了Ruby 中异常类的继承结构,你不必全部记住他们,事实上这样类中没有定义很多方法,你唯一要知道的事下列的一场都属于Ruby中的标准异常,你可以继承或者扩展他们:
Object
 +--Exception
     +--NoMemoryError
     +--ScriptError
     |   +--LoadError
     |   +--NotImplementedError
     |   +--SyntaxError
     +--SecurityError         # Ruby1.8中的标准异常
     +--SignalException
     |   +--Interrupt
     +--SystemExit
     +--SystemStackError      # Ruby1.8中的标准异常
     +--StandardError
         +--ArgumentError
         +--FiberError        # Ruby 1.9中的新增异常
         +--IOError
         |   +--EOFError
         +--IndexError
         |   +--KeyError      # New in 1.9
         |   +--StopIteration # New in 1.9
         +--LocalJumpError
         +--NameError
         |   +--NoMethodError
         +--RangeError
         |   +--FloatDomainError
         +--RegexpError
         +--RuntimeError
         +--SystemCallError
         +--ThreadError
         +--TypeError
         +--ZeroDivisionError
Exception类定义了两个方法用于返回异常信息,message方法用于返回“可读”的异常信息字符串,如果Ruby程序出现了一个未捕获的一场,这个消息会显示给最终用户,通常用于程序员调试代码。另一个方法是backtrace,这个方法返回一个数组,数组中包含了调用的堆栈信息:
filename : linenumber in methodname
以上的代码中filename说明出错的文件位置,linenumer说明那一行出现了错误,methodname说明由那个方法引起的异常。如果你要定义自己的异常类,可以通过set_backtrace来自定义这个信息。
你可以通过扩展当前异常类来定义自己的异常类:
class MyError < StandardError; end

你可以通过raise或者fail来抛出一个异常,有以下几种方式:
  • 如果raise没有参数,那么Ruby会创建一个RuntimeError(不带任何消息)的异常类,并抛出它。
  • 如果raise有一个参数,并且这个参数是一个Exception对象,那么Ruby会直接抛出这个异常对象,通常我们不这样作 。
  • 如果raise有一个参数,并且这个参数是一个字符串,那么Ruby会创建一个RuntimeError对象并将字符串的内容作为异常的消息,我们经常这样使用。
  •  如果raise的第一个参数是一个对象,且这个对象有一个exception方法,那么Ruby会调用这个方法并抛出这个方法返回的异常对象。第二个参数可以是一个字符串,这个字符串会传递给第一个对象的exception方法的第一个参数,作为异常消息。
def factorial(n)                 # 定义一个函数
  raise "bad argument" if n < 1  # 如果条件不满足就抛出异常
  return 1 if n == 1             # factorial(1) 为 1
  n * factorial(n-1)             # 继续计算
end
上面的例子中raise只有一个字符串参数,下面是用其它方式抛出的异常:
raise RuntimeError, "bad argument" if n < 1
raise RuntimeError.new("bad argument") if n < 1
raise RuntimeError.exception("bad argument") if n < 1
就像上面的代码那样,你可以通过new或者exception关键字来创建你自己的异常,这两个方法的字符串参数是异常显示的消息。
Ruby中捕获异常通过rescue关键字,通常一个rescue位于begin和end之间,如下例:
begin
  # 这里放执行代码.
  # 通常他们可以正常的执行 
rescue
  # 这里是rescue子句,如果上面的代码出现异常,那么就跳转到这里,
  # 可以在这里放置其他代码来处理异常 .
end

当然我们可以命名异常对象,如果定义全局变量$!并且require 'English',那么你可以使用$ERROR_INFO 来命名异常,当是一个更好的方法是使用变量:
begin                                # 在这个块中捕获异常
x = factorial(-1)                  # 这里我们故意加入了非法的参数
rescue => ex                         # 将异常对象存储在一个变量ex中
  puts "#{ex.class}: #{ex.message}"  # 捕获这个异常并打印异常消息
end                                  # 结束块
注意这里声明的异常变量不仅仅在异常处理范围,一旦定义了这个变量,你可以在块的外部使用它。
还要注意的是上面这种方式只能捕获StandardError。如果你要捕获StandardError以外的异常,或者你要捕获一个你自定义的异常类型,你必须声明这个异常,或者使用rescue Exception的方式捕获任何类型的异常:


rescue Exception #捕获任何类型的异常
rescue ArgumentError => e #捕获ArgumentError异常
rescue ArgumentError, TypeError => error #将多个异常赋值到一个异常变量中

我们同样可以以一种分支的方式分别处理不同的异常:

rescue Exception #捕获任何类型的异常
rescue ArgumentError => e #捕获ArgumentError异常
rescue ArgumentError, TypeError => error #将多个异常赋值到一个异常变量中
我们前面讲过的retry关键字可以在rescue中使用,这样使用的目的是,有的时候引发的异常只是短暂异常,可能过一会儿就可以正常运行了,比如,当前服务器可能无法访问但是过一会儿就正常了,下面是一个访问网络的例子,它遇到网络异常后尝试再次访问:
require 'open-uri'

tries = 0       # 访问一个URL等待的时间
begin           # 块开始部分
   tries += 1    # 尝试连接 URL并打印连接信息
  open('http://www.example.com/') {|f| puts f.readlines }
rescue OpenURI::HTTPError => e  # 如果得到一个HTTP error
  puts e.message                # 打印异常信息
  if (tries < 4)                # 判断尝试的次数...
    sleep(2**tries)             # 等待 2, 4, 或者 8 秒钟
    retry                       # 然后再试一次!
  end
end
这里还有一个else子句,你可能认为else是在rescue执行失败后执行的语句,其实不然,实际上else子句部分的代码是在当begin部分的代码可以没有异常的正常执行后执行的代码。老实说else在rescue中并不经常使用,如果begin中没有rescue的话使用else是没有意义的。
另外一个重要的关键字是ensure,在这个子句中的代码无论如何都会被执行,比如你要关闭一个文件、一个数据库连接或者提交数据库执行语句。如果ensure在方法中,那么它的值不会作为方法的返回值,也就是说,在你调用一个方法的时候实际上会先执行ensure中的语句然后再执行begin中的语句。不过,如果你在ensure中使用了return关键字那么方法的返回值会被替换为ensure的返回值。
begin
  return 1     # 方法的返回值
ensure
  return 2     # 最终会返回这个值
end
事实上rescue,ensure这些关键字不仅可以用于begin/end之间,还可以用于def定义的模块、类、方法。
下面是resuce子句的一个变体,如果resuce前面的代码出现异常或者raise一个异常,那么resuce后面的代码就会执行:
y = factorial(x) rescue 0
上面的代码等同于:
y = begin
      factorial(x)
    rescue
      0
    end
这种写法的好处是可以省略begin/end,但必须在一行里完成,而且这种变态中只能处理StandardError异常,而不能出来其它类型的异常。

Wednesday, October 22, 2008

(7)更改控制流程

除了我们上面讲的条件控制语句、循环、迭代器,Ruby还提供了其他的语法来更改程序的控制流程:
  • return:会导致退出一个方法,或者给调用者返回一个值。
  • break:退出一个循环(或者迭代器) 。
  • next:跳过当前循环中的一个迭代,执行下一个迭代。
  • redo:重新开始当前的循环。
  • retry:重新执行当前循环的一个迭代,它还可以用于异常控制语句中。
  • throw/catch:异常控制语句 。
return
return就是退出当前的方法并给调用者返回一个值,如果你熟悉C或者Java语言,你也许已经明白这个关键字是干什么的了,不过,等等,return 在块中的使用还是不太一样的。
return 后面一般会跟一个表达式或者用逗号分隔的多个表达式。如果没有表达式则说明当前方法返回nil。如果有一个表达式,那么方法的返回值就是这个表达式的返回值,如果有多个表达式,那么方法的返回值就是这一系列表达式的返回值。不过Ruby的方法中通常会忽略这个关键字,因为默认情况下方法最后一行的表达式的值就是方法的返回值。使用return 一般有两种情况,要么是你准备在程序中的某个分支就返回方法,要么是你准备返回多个值。
def double(x)
  return nil if x == nil   # 提前返回
  return x, x.dup          # 返回多个值
end
当我们刚开始学习Ruby中的时,我们通常把它想象成一个内嵌的函数或者一个迷你方法。你可能会认为块的return会返回迭代器yield给他的值,其实不然,return一般用于内嵌方法并直接把内嵌方法的返回值返回给迭代器,如果遇到return这个块就会结束并把最终值返回给迭代器:
def find(array, target)
  array.each_with_index do |element,index|
    return index if (element == target)  # 将找到的值返回给迭代器
  end
  nil  # 如果找不到元素就返回nil
end
break
break在循环中意味着循环的中断,或者说跳出循环,如果你是一个C,Java他们的含义是一样的:
while(line = gets.chop)     # 开始循环
break if line == "quit"   # 跳出循环...
  puts eval(line)
end
puts "Good bye"             # ...循环结束并直接跳转到这里
记住Ruby中的break是可以有值得,你可以在break后跟一个或多个表达式,如果是多个表达式,那么break返回的是一个数组。
next
循环中的next意味着跳过当前的一个迭代:
while(line = gets.chop)     # 开始循环
  next if line[0,1] == "#"  # 如果遇到#就跳过当前迭代执行下一迭代
puts eval(line)
end
如果在块中使用next意味着立即跳出当前的块,并把控制权交给迭代器以便执行下一个迭代:
f.each do |line|              # 开始迭代
  next if line[0,1] == "#"    # 如果遇到#就跳出并执行下一个迭代
  puts eval(line) 
end
同样,next也可以跟返回值,将当前迭代的一个值返回给迭代器:
squareroots = data.collect do |x|
  next 0 if x < 0  # 如果是负数就返回0
  Math.sqrt(x)
end
redo
redo也是中断当前迭代,但是和next不同他不会跳过当前迭代而继续执行,它会使得循环从开始位置执行。
i = 0
while(i < 3)   # 打印结果是"0123" 而不是 "012"
  # 当遇到redo,循环从此处开始执行。
  print i
  i += 1
  redo if i == 3
end
retry 
这个关键字已经在Ruby1.9中弃用。
throw/catch
这两个关键字可以想象为一个多级的break,可以被当作是一种有标记的break。下面是一个例子:
for matrix in data do             # data是一个多层嵌套的数据结构.
  catch :missing_data do          # 标记这个位置,这样我们可以在此处退出.
    for row in matrix do
      for value in row do
        throw :missing_data unless value # 从当前的两个循环退出.
        # 否则执行其他的代码.
      end
    end
  end
  # 这里结束 matrix 迭代.
  # 我们同样在这里结束一个迭代,如果missing_data被抛出.
end

(6)Ruby中的块Blocks

在我们前面介绍迭代器的时候已经用到了块,块类似于方法调用,但是还是有一些区别的,本节将详细介绍块。
块的语法结构
块是不能单独存在的,它必须出现在方法调用之后,也就是说如果块不在一个迭代器或者方法调用中没有yield那么这个块会被忽略。块以{}定义,或者使用do end 关键字,块的一个花括号{ 或者do 关键字必须与迭代器或者方法调用在同一行,否则Ruby解析器无法把它当作迭代器或者方法调用的一部分:
# 打印1到10的数字
1.upto(10) {|x| puts x }   # 块与方法调用位于同一行
1.upto(10) do |x|          # 使用 do/end关键字
  puts x
end
1.upto(10)                 # 没有指定块
{|x| puts x }             # 语法错误:块没有位于方法调用之后
一个通常的做法是,如果块只有一行接单的逻辑则使用{},如果是多行的逻辑,就使用do/end关键字方式。值得一提的是如果你的方法存在参数,那么使用{的时候要给参数加上括号(),如果你的参数没有括号,那么你应该使用do/end方式:
1.upto(3) {|x| puts x }    # 加括号的参数使用{}
1.upto 3 do |x| puts x end # 没有括号参数的方法使用,do/end
1.upto 3 {|x| puts x }     # 语法错误!
块可以有参数,不过它的参数要放在||之间并用逗号分隔,除此之外它和方法参数非常相似:
# 字典对象的each方法有两个参数
hash.each do |key, value|   # 循环调用字典对象的(key,value) 
  puts "#{key}: #{value}"   # 打印key和value
end                         # 块结束
块的返回值
我们在前面提到的块调用中一再说到yield值,但是没有说到返回值,实际上块是有返回值的,返回值就是块的最后一个表达式的值,注意,不要在块中使用return关键字,这个关键字意味着强制结束你的块逻辑执行,有些时候这是不好的,如果你希望块能够返回更多的值你应该使用next关键字:
array.collect do |x|
  next 0 if x == nil  # 如果x==nil返回0
  next x, x*x         # 返回两个值
end
块的变量范围
块内可以有自己的局部变量,这些变量只在块的范围你有效,但是,如果块内的变量名和块外的变量名相同,这时Ruby不会在块内重新声明变量,而是使用块外部的变量。有时候这对我们来说是很有用的,比如:
total = 0   
data.each {|x| total += x }  # 计算数组内所有元素的合计值
puts total                   # 打印合计值
但是有的时候这样不好,比如,如果一个块的参数和一个已经存在的变量重名,那么Ruby不会创建一个新的变量而是使用那个已经存在的变量。比如:
1.upto(10) do |i|         # 十条记录
  1.upto(10) do |i|       # 每条记录有十列
    print "#{i} "         # 打印每一列
end
  print " ==> Row #{i}\n" # 这里试图打印行号,但是实际得到是列号
end
在Ruby1.9中没有这种情况,块中的变量只在当前块中有效,而且不共享块以为定义的变量,如果你打开了-w选项,Ruby会警告你块变量与已经存在的变量重复。Ruby1.9中从语法定义上根本的解决了这个问题,块参数之间使用分号;分隔来表示这是一个块的参数:
x = y = 0            # 局部变量
1.upto(4) do |x;y|   # x和y现在是块的局部变量
                     
  y = x + 1          # 使用块局部变量
puts y*y           # 打印4, 9, 16, 25
end
[x,y]                # => [0,0]:块没有修改外部的局部变量
块可以有多个参数和多个变量下面是一个例子:
hash.each {|key,value; i,j,k| ... }

(5)枚举对象

你可以在Ruby1.8中通过require 'enumerator' 来使用枚举对象,在Ruby1.9中这个对象是内建的,你不用单独导入一个库。枚举对象位于Enumeration::Enumerator,你可以通过new 关键字来创建一个枚举对象,但是通常我们是通过在其它对象上使用to_enum或者enum_for来创建枚举对象。有时候你要为一个方法传递一个数字参数,但是你又担心这个方法可能会改变你的数组,那么通过to_num方法可以创建这个数字的代理对象,你就不必有这样的担心了,因为这时这个对象是不可变的。比如:
process(data.to_enum)  # 而不是使用 process(data)
你还可以给enum_for方法传递参数,比如对象String对象你希望是使用其each_char来获得一个数组:
s = "hello"
s.enum_for(:each_char).map {|c| c.succ }  # => ["i", "f", "m", "m", "p"]

(4)自定义迭代器

定义一个迭代器类似于方法调用,只不过我们这里是调用中的代码。实现这一功能需要使用yield方法,下面是一个例子,她非常简单实现了调用两次中代码的功能:
def twice
yield
yield
end

迭代器可以有参数,参数用括号包含并用逗号分隔,当然按照方法定义的规则,括号是可以省略的。下面是一个例子:
# 下面的方法定义了一个迭代器,每次迭代输出一个计算值,输出的结果是m*i+c,要求
# i的值必须在0到n-1之间

def sequence(n, m, c)
i = 0
while(i < n) #循环n次
yield m*i + c # 调用块,并把计算结果传递给块参数
i += 1 # 每次i的值加1
end
end

# 下面是调用的方法,结果将打印 1, 6, 和 11
sequence(3, 5, 1) {|y| puts y }

下面是另一个例子,它传递了两个参数,值得注意的是在这个迭代器中使用了另一个迭代器:
def circle(r,n)
n.times do |i| # 注意这里使用了另一个迭代器
angle = Math::PI * 2 * i / n
yield r*Math.cos(angle), r*Math.sin(angle)
end
end

# 调用这个迭代器将打印:
# (1.00, 0.00) (0.00, 1.00) (-1.00, 0.00) (-0.00, -1.00)
circle(1,4) {|x,y| printf "(%.2f, %.2f) ", x, y }

yield关键字非常类似于方法调用,但是你不能像方法一样给块参数传递另一个块。有时候你定义了一个迭代器,但是如果在调用它的时候没有给出块部分,这回引发一个异常。你可以定义一个缺省行为,在每一个给出块的时候执行你的缺省行为。这需要用到方法block_given?或者iterator?,这两个方法是kernel中定义的方法,所以你可以当作全局函数那样使用:
# 根据公式 m*i+c迭代
# 如果块存在yeild值
def sequence(n, m, c)
i, s = 0, [] # 初始化变量
while(i < n) # 循环n次
y = m*i + c # 计算值
yield y if block_given? # Yield, 如果有块
s << y # 存储值
i += 1
end
s # 返回数组
end

(3)迭代器和枚举对象

尽管我们前面说到的while,until,for循环是Ruby的核心部分,但是通常情况下我们是用的是另一个更特殊的方式,称为迭代器,迭代器是Ruby中非常值得关注的功能,很多介绍Ruby的文章都会出现类似这样的例子:
3.times { puts "thank you!" }  # 打印3次 感谢
data.each {|x| puts x } # 打印data中的每个元素
[1,2,3].map {|x| x*x } # 计算数组的平方
factorial = 1 # 计算n的阶乘
2.upto(n) {|x| factorial *= x }
times,each,map,upto都是迭代器,他们与紧随其后的进行交互。隐藏在这些复杂操作背后的是yield操作,yield子句会临时从迭代器方法获得一个控制,并把它交给迭代器调用的方法。特别是当控制逻辑贯穿整个代码块的迭代调用,当代模块结束迭代器方法会收回这个控制接着执行yield后的内容。下图是迭代器yield的执行过程:
如你所见,一个代码块是可以有参数的,||中包含的就是参数,就像你在方法定义中()中的参数一样--你可以定义一些列参数,所以yield子句就相当于方法调用。值得注意的是迭代器并不一定全部用于迭代,不如Ruby1.9中定义的tap方法。或者File.open,这个方法是一个迭代器,但是如果其中没有任何参数传入的话,它只代表你打开了一个文件,在open代码块结束的时候文件自动被关闭,这很好的避免了由于忘记关闭程序而带来的问题。
Numeric迭代
Ruby为整数类型定义了3中迭代器,upto方法执行从当前整数对象到传入的整数参数之间的范围内,每一个元素的循环调用。比如
4.upto(6) {|x| print x}   # => 打印"456"

一般来说一个n.upto(m)将执行m-n+1 次循环。downto方法类似于upto不过正好相反,它是从大的数字向小的数字循环。
Integer.times方法执行n次循环,比如:
3.times {|x| print x }    # => 打印"012"

如果你想使用浮点数进行迭代,你可以使用step方法,比如下面的例子中数字从0开始每次增加0.1直到数值达到Math::PI的值,并计算每个迭代只得sin值:
0.step(Math::PI, 0.1) {|x| puts Math.sin(x) }
枚举对象
Array,Range,Hash以及其他的一系列类,定义了一个each方法,这个方法会将集合内的每一个元素传递给代码块。这可能是Ruby中最常用的操作:
[1,2,3].each {|x| print x }   # => 打印"123"
(1..3).each {|x| print x } # => 打印"123" 等同于 1.upto(3)
不仅仅是这些类,Ruby中的IO库定义了each方法,这样你就可以循环的读取文件中的每一行并对其进行处理:
File.open(filename) do |f|       # 打开指定的文件,并将其内容赋值到变量f中
f.each {|line| print line } # 打印f的每一行
end # 结束块并关闭文件
很多类还基于each方法定义了更多专用的方法,比如each_with_index就是其中之一:
File.open(filename) do |f|
f.each_with_index do |line,number|
print "#{number}: #{line}"
end
end
除此之外,枚举对象还定义了一系列其他有用的方法,collect,select,reject和inject。collect方法循环枚举对象中的每个元素然后根据代码块中的计算结果创建一个新的枚举对象
squares = [1,2,3].collect {|x| x*x}   # => [1,4,9]

select 方法用于筛选块中条件判断为true的元素,并返回筛选的结果:
evens = (1..10).select {|x| x%2 == 0} # => [2,4,6,8,10]

reject 方法与select方法正好相反,他筛选块中条件判断为false或nil的元素,并返回筛选的结果:
odds = (1..10).reject {|x| x%2 == 0}  # => [1,3,5,7,9]


inject 方法有点复杂,解释一下,它有两个参数,第一个参数是这个迭代器的计算结果值,第二个参数代表迭代对象的循环时的每一个元素,每次迭代的返回值是第一个参数,在迭代结束的时候返回值就是最终两个参数的迭代计算结果。inject还有一个初始化参数房在括号中,如果这里有一个参数那么它将使枚举对象的第一个元素:
data = [2, 5, 3, 4]
sum = data.inject {|sum, x| sum + x } # => 14 (2+5+3+4)
floatprod = data.inject(1.0) {|p,x| p*x } # => 120.0 (1.0*2*5*3*4)
max = data.inject {|m,x| m>x ? m : x } # => 5 (largest element

Tuesday, October 21, 2008

(2)循环语句

Ruby提供三种简单的循环while,until和for,while循环执行“当”条件表达式为true,until循环执行“直到”条件表达式为true:
x = 10               # 初始化一个循环计数器变量
while x >= 0 do # 如果x大于等于0就之一执行循环
puts x # 打印x的值
x = x - 1 # 递减x的值
end # 循环结束

# 执行这个循环知道x的值大于0就结束循环
x = 0 # 初始化循环计数器
until x > 10 do # 开始循环
puts x
x = x + 1
end # 结束循环
和if语句一样Ruby也提供了一个在一行代码中完成循环的变体:
x = 0                          # 初始化循环计数器
puts x = x + 1 while x < 10 # 执行循环

a = [1,2,3] # 初始化一个数组
puts a.pop until a.empty? # 循环的从数组中移除元素,知道这个数组为空。
这里有一种特殊情况,如果循环执行的代码是一个begin end包含起来的一段代码块,那么代码块的内容会先于循环逻辑判断:
x = 10              # 初始化循环计数器
begin # 在循环表达式判断之前,这段代码会至少执行一次
puts x
x = x - 1
end until x == 0
注意,如果你的代码块不是放在begin end之间,而是放在()中,那么不会在until判断之前执行代码块中的内容:
x = 0               # 初始化
( # 这种方式的代码片段如果条件表达式判断失败不会执行。
puts x
x = x - 1
) until x == 0
最后一个循环是for/in循环,它一般用于数组或者Range,字典对象,用于在这些对象的元素中迭代取值并执行代码,和while until一样,do关键字是可选的,for关键字后跟一个变量,或者用逗号分隔的多个变量,用于代表数组或字典中的元素,所有数组或者字典中的元素全部遍历一遍之后,循环就结束:
# 打印数组中的所有元素
array = [1,2,3,4,5]
for element in array
puts element
end

# 打印字典对象的键/值
hash = {:a=>1, :b=>2, :c=>3}
for key,value in hash
puts "#{key} => #{value}"
end
实际上for /in 循环是基于for语句内容对each方法的实现,比如上面的代码可以写成这样:

hash = {:a=>1, :b=>2, :c=>3}
hash.each do |key,value|
puts "#{key} => #{value}"
end

(1)条件判断语句

这一节要说的是条件控制语句,if, else ,elsif,unless,end。是Ruby中所有的对象都是有值的即使是一个条件判断语句。和大多数语句一样Ruby中条件判断语句用前面提到的关键字实现。比如:
if 条件表达式
要执行的代码
end


如果条件表达式的值为nil和false以外的任何值:if 和end之间的代码都会被执行,条件表达式之后必须换行后再写入要执行的代码,否则的话你需要一个额外的关键字then来分隔出哪部分是条件表达式,哪部分是要执行的代码。
# 如果x小于10 就给它加1
if x < 10 # 通过换行分隔
x += 1
end
if x < 10 then x += 1 end # 通过then关键字分隔

当然,在then关键字之后换行也是允许的:
if x < 10 then
x += 1
end

else关键字用于条件的跳转,如果if的条件不成立那么就执行else 部分的代码。
if data         # 如果data不为空
data << x # 那么在data中增加元素.
else # 否则...
data = [x] # 创建一个新的数组来保存这个元素.
end # 条件判断结束.

elsif关键字相当于else if ,解释一下就是“否则的话,那么如果。。。”
if x == 1
name = "one"
elsif x == 2
name = "two"
elsif x == 3 then name = "three" #elsif后也可以加then关键字
elsif x == 4; name = "four"#可以用分号替带换行
else
name = "many"
end

我们刚才说过Ruby中所有的对象都是有值得包括条件判断语句,条件判断语句返回的值,就是满足条件的代码块最后一行的值:
name = if    x == 1 then "one"
elsif x == 2 then "two"
elsif x == 3 then "three"
elsif x == 4 then "four"
else "many"
end
刚才我们提到,if表达式必须以换行或者then 加end的方式出现,Ruby中还提供了另外一个变体,你可以把要执行的代码放在if前面,条件表达式放在if的后面,then和end都可以不写,不过这种写法要求整个语句只能位于一行中:
puts message if message    # Output message, if it is defined

除了if语句Ruby还提供了一个unless语句,它的结构和if语句相似,但是执行与if语句相反的判断操作,也就是说如果unless后的表达式为false或nil那么就执行其后的代码:
s = unless o.nil?                        # 换行分隔
o.to_s
end
s = unless o.nil? then o.to_s end # then 关键字分隔

不过,unless没有类似elsif这样的语句。
最后在来提提Ruby中的case语句,如果你是Java程序员,你一定用过switch语句,他们的功能是类似的,不过case语句更强的,因为基于case语句的条件表达式可以是任何对象类型。
case
when x == 1
"one"
when x == 2
"two"
when x == 3
"three"
end

你也可以在when 后的条件表达式 后加then关键字来省去换行。或者更简洁的写法(如果你只判断一个变量的值):
name = case x
when 1 # 只比较x的值
"one"
when 2 then "two" # then关键字替代了换行
when 3; "three" # 分号替代了换行
else "many" # else用于提供缺省值
end

case语句中还可以包含多个条件表达式,表示满足其中任何一个条件都可以执行其后的代码:
case
when x == 1, y == 0 then "x is one or y is zero" # 用逗号分隔多个条件
when x == 2 || y == 1 then "x is two or y is one" # ||看起来更容易理解
end
我们前面讲过===在Range中可以判断一个值是否包含在其中,还可以判断一个对象是否是一个类的实例,在case语句中也可以这样使用:
x=1

case
when Numeric===x
print "yes"
else
print "no"
end

Ruby中同样支持?:的简写方式来完成一个条件判断,其中?相当于then,:相当于else:
def how_many_messages(n) # Handle singular/plural 
"You have " + n.to_s + (n==1 ? " message." : " messages.")
end