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

No comments: