在迭代、块或for语句中使用retry,意味着重启迭代器。
同时迭代器的参数也将被重新计算。
for i in 1..5
retry if some_condition # 从 i == 1 开始重新执行
end
# 用户定义的 "until循环"
def UNTIL(cond)
return if cond
yield
retry
end
---------------------------------------------------------
异常处理语句
raise
例:
raise "you lose" # 引发RuntimeError异常
# 下面两个将引发SyntaxError异常
raise SyntaxError, "invalid syntax"
raise SyntaxError.new("invalid syntax")
raise # 再次引发上一个异常
语法:
raise
raise message或exception
raise error_type, message
raise error_type, message, traceback
引发异常。
第一句将再次引发上一个异常。
第二句中,若参数是字符串的话,就把它当作错误信息(message)再引发RuntimeError异常。
若参数为异常对象则引发该异常。
第三句中,将引发第一个参数所指的异常,并以第二个参数的内容作为错误信息。
第四句中,第三参数装载的是源自于$@或caller的堆栈信息,它指明发生异常的地点。
可以使用begin表达式的rescue部分来捕捉异常。这时使用rescue error_type => var就可以得到异常对象。您还可以从内部变量$!中获得这个对象。另外,变量$@中装载的是发生异常的源代码位置。
raise并不是Ruby的保留字,它是Kernel模块中定义的函数式的方法。
begin
异常处理的开始,
这个其实就像try catch finally一样,
其中
rescue === catch
ensure === finally
语法:
begin
表达式..
[rescue [error_type,..] [=> evar] [then]
表达式..]..
[else
表达式..]
[ensure
表达式..]
end
若给出了rescue部分(可以有若干个)的话,就可以在发生异常时捕捉到它。
若存在与异常类型一致的rescue部分的话,就执行rescue的内容。
可以使用$!来查看异常的情况。另外,若事先设定了变量evar的话,
它也可以像$!一样存储那些异常的信息。
begin
raise "error message"
rescue => evar
p $!
p evar
end
# => #<RuntimeError: error message>
#<RuntimeError: error message>
rescue部分使用Object#kind of?来判断刚才的异常的类是否就是自己期待的异常类,
或者这二者是否处于父类/子类的关系。
若error_type被省略,则将捕捉StandardError的子类中的所有异常。
Ruby的内部异常(除了SystemExit和Interrupt这些退出命令以外)
是StandardError的子类。
请参考异常类来了解异常类的层次关系。
在rescue
部分中,error_type与普通的参数一样接受计算,若符合的话就执行
相应部分的内容。
若error_type的计算值既非类又非模块的话,则引发TypeError异常。
若运行过程中没发生异常,则开始计算可选的else部分。
若存在ensure
部分的话,则在begin表达式结束之前一定会计算它。
begin
表达式整体的计算值取决于,begin的内容部分/rescue部分/else部分
中最后被计算的句子的值。若各部分中均无语句时,其值为nil。不管怎样,
ensure部分的值始终会被忽略。
例:
open("nonexistent file")
rescue STDERR.puts "Warning: #$!"
语法:
表达式1 rescue 表达式2
若表达式1中发生异常时就计算表达式2。这等同于下例。
不能指定想捕捉的异常类。(也就是说,只能捕捉StandardError异常类的子类了)
begin
表达式1
rescue
表达式2
end
在包括rescue修饰句的表达式中,若没发生异常则返回表达式1的值,
若发生异常则返回表达式2的值。但在大多数场合中,因为考虑到优先度的问题,
所以需要使用括号将整个表达式括起来。
var = open("nonexistent file") rescue false
p var
=> nil # 因为只定义了一个空变量var
var = (open("nonexistent file") rescue false)
p var
=> false
是传递给某方法的参数时,有必要使用双重括号。
p(open("nonexistent file") rescue false)
=> parse error
p((open("nonexistent file") rescue false))
=> false
return
例:
return
return 12
return 1,2,3
语法:
return [表达式[`,' 表达式 ... ]]
结束方法的运行,且把表达式的值设定为方法的返回值。若给出了2个以上的表达式,
则将把这些表达式化为一个数组,然后把该数组设定为方法的返回值。若省略表达式,
将返回值设为nil。
BEGIN
语法:
BEGIN '{' 语句.. '}'
注册初始化例程(routine)。BEGIN块所指定的语句的执行顺序将先于该文件中任何语句。
若有多个BEGIN块的话,将按照出现顺序依次执行。
BEGIN块在编译时被注册。也就是说,同一条语句只会被注册一次。
if false
BEGIN { p "begin" }
end
# => "begin"
BEGIN块引入了独立的局部变量作用域,因此不能和外部共享局部变量。
为了与块外交换信息,必须借助于常数或全局变量。
BEGIN { $foo, foo = true, true }
p $foo # => true
p foo # undefined local variable or method `foo' for main:Object (NameError)
BEGIN不能出现在方法定义表达式中,否则会引发 parse error。
def foo
BEGIN { p "begin" }
end
# => -:2: BEGIN in method
END
语法:
END '{'
语句..
'}'
注册“善后”例程。END块中指定的语句会在解释器结束前得到执行。
若注册了若干END块的话,则与注册时相反的顺序依次执行这些块。
END { p 1 }
END { p 2 }
END { p 3 }
# => 3
2
1
把END块置入循环中,也只会注册一次。
5.times do |i|
END { p i }
end
# => 0
若把END块置入方法定义表达式中会引起警告。
def foo
END { p "end" }
end
p foo
# => -:2: warning: END in method; use at_exit
nil
"end"
END块与BEGIN块不同的是,它同周围部分共享作用域。也就是说,它的作用域同迭代器一样。
若END块中发生了异常,将中断该块。但解释器并不结束,只是发出信息,
并且试图处理完所有的善后例程。
例:
END { p "FOO" }
END { raise "bar"; p "BAR" }
END { raise "baz"; p "BAZ" }
=> baz (RuntimeError)
bar (RuntimeError)
"FOO"