条件

if

if else 的写法

if data
  data << x
else
  data = [x]
end

if elsif 的写法

if x == 1
  name = "one"
elsif x == 2
  name = "two"
elsif x == 3 then name = "three"
elsif x == 4; name = "four"
else
  name = "many"
end

if 语句的返回值来做为赋值

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 简短写法

puts message if message # 输出 message,如果有定义 message

unless

unless 当表达式是 falsenil,就执行下面的内容,用法类似于 if,除了没有 elsif 这样的写法

unless condition
  code 
end

unless condition
  code
else
  code
end

code unless condition
case

case

if/elsif/else 的另一种写法

name = case x
       when 1
         "one"
       when 2 then "two"
       when 3; "three"
       else "many"
       end

需要注意一点,case 中的表达式怎么和 when 中值进行比较的,是通过 === 运算符来完成的,上面的写法实际就是按下面这种方式运行的

name = case
       when 1 === x then "one"
       when 2 === x then "two"
       when 3 === x then "three"
       else "many"
       end

Class 类中定义 === 方法来检测对象是否某个类的实例,所以就可以写如下的代码

puts case x
     when String then "string"
     when Numeric then "number"
     when TrueClass, FalseClass then "boolean"
     else "other"
     end

?:

?: 是一种更简洁的条件表达式

def how_many_messages(n)
  "You have " + n.to_s + ( n == 1 ? " message." : " messages.")
end

循环

while

while 基本的写法

x = 10
while x >= 0 do
  puts x
  x = x - 1
end

while 简洁的写法

x = 0
puts x = x + 1 while x < 10

until

until 基本的写法

x = 0
until x > 10 do
  puts x
  x = x + 1
end

until 简洁的写法

a = [1, 2, 3]
puts a.pop until a.empty?

for in

for in 循环每一次都调用对象的 each 方法得到值

hash = { :a => 1, :b => 2, :c => 3 }
for key,value in hash
  puts "#{key} => #{value}"
end

each

还比如直接用 each 来的帅

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

块就是跟在方法后面在 { }do end 中的代码块

1.upto(10) { |x| puts x }
1.upto(10) do |x|
  puts x
end

在定义方法的时候可以通过 yield 调用块,如下面的自定义方法

def sequence(n, m, c)
  i = 0
  while (i < n)
    yield m * i + c # 调用块,传递值给块
    i += 1
  end
end

在方法定义中可以通过 block_given? 来判断是否有块

def sequence(n, m, c)
  i, s = 0, []
  while (i < n)
    y = m * i + c
    yield y if block_given?
    s << y
    i += 1
  end
  s
end

块中最后一行表达式的值就是块的返回值,从上面的例子可以看到方法定义中可以利用 yield 调用获取到块的返回值来进行相应的操作

words.sort! { |x, y| y <=> x }

块中定义的变量的作用域就在块中

其他的流程控制的

return

return 对于无论嵌套的块有多深,总是将最近的包围的方法返回

def find(array, target)
  array.each_with_index do |element, index|
    return index if (element == target) # 找到了就作为 find 的返回值
  end
  nil # 没有找到,就返回 nil
end

break

break 会结束循环,执行循环块后的第一条语句

while (line = gets.chop) # 循环开始
  break if line == "quit" # 退出循环
  puts eval(line)
end
puts "Good bye" # 然后从这里继续执行

next

next 会结束当前这一步的循环执行,进行下一步的循环执行

while(line = gets.chop) # 循环开始
  next if line[0, 1] == "#" # 如果是注释符号,到下一步循环
  puts eval(line)
  # 然后从这里继续执行
end

redo

redo 会重新执行当前的这一步循环

i = 0
while (i < 3) # 输出 "0123" 而不是 "012"
  # redo 执行后会从这里继续执行
  print i
  i += 1
  redo if i == 3
end

异常

异常对象是 Exception 类的实例或其子类的实例。需要知道的是大多数的异常子类都是继承于 StandardError 类,这是 Ruby 程序需要处理的异常

Exception 类定义了两个方法来获取异常的相关信息

  • message 方法返回易于人阅读的异常信息
  • backtrace 方法返回异常是在什么地方开始的,以及方法调用的层级引用

抛出异常

调用 raise 创建异常,若只有一个字符串参数时,会创建一个新的 RuntimeError 对象,字符串参数就是异常的 message

def factorial(n)
  raise "bad argument" if n < 1
  return 1 if n == 1
  n * factorial(n - 1)
end

调用 raise 时,若第一个参数是类,并且该类有一个 exception 方法,raise 就会调用这个 exception 方法然后抛出此方法返回的异常对象,因为 Exception 类定义了 exception 方法,所以你可以将任何异常类作为 raise 的第一个参数,还可以添加第二个参数传递给 exception 方法作为异常的 message

raise RuntimeError, "bad argument" if n < 1

调用 raise 时,若第一个参数是异常对象,那就直接将异常对象抛出

raise RuntimeError.new("bad argument") if n < 1
raise RuntimeError.exception("bad argument") if n < 1

自定义异常

自定义异常类,通常继承 StandardError

class MyError < StandardError
end

捕获异常

捕获异常,下面写法中 rescue 捕获任何 StandardError 的异常,其他的异常会被忽略掉

begin
  # 任意的 Ruby 表达式
rescue
  # 如果有异常抛出,流程就会转到这里
end

rescue 中,全局变量 $! 代表被处理的异常对象,可以通过下面的方式来为异常对象指定一个变量名称

begin
  x = factorial(-1)
rescue => ex
  puts "#{ex.class}: #{ex.message}"
end

捕获特定类型的异常

begin
  x = factorial(1)
rescue ArgumentError => ex
  puts "Try again with a value >= 1"
rescue TypeError => ex
  puts "Try again with an integer"
end

如果在 rescue 语句又发生了异常,原来被捕获的异常会被抛弃,新的异常传播会从这里开始

在异常捕获时,else 中的语句在 begin end 语句运行没有出现异常的时候才会执行

在异常捕获时,ensure 中的语句都会执行,无论前面异常捕获语句中的代码发生了什么

一个典型的方法定义中使用异常语句

def method_name(x)
  # 任意的 Ruby 表达式
rescue 
  # 如果有异常抛出,流程就会转到这里
else
  # 如果没有异常抛出,流程就会转到这里
ensure
  # 无论有没有异常抛出,流程就会转到这里
end

rescue 的一种简洁用法,不能指定异常类名,只能捕获 StandardError 异常

y = factorial(x) rescue 0