深入探索
布尔(Boolean)测试
and &&
这些运算符只有在判断左侧结果为 true 时,会继续判断右侧,and
的优先级比 &&
低。
or ||
这些运算符只有在判断左侧结果为 false 时,会继续判断右侧,or
的优先级比 ||
低。
not !
布尔值的否操作,即值为 false 时返回 true,值为 true 时返回 false。
使用两种不同的布尔运算符时要小心。由于优先级的差异,测试将以不同的顺序进行判断,并可能产生不同的结果。
思考以下代码:
# Example 1
if (1==3) and (2==1) || (3==3) then
puts('true')
else
puts('false')
end
# Example 2
if (1==3) and (2==1) or (3==3) then
puts('true')
else
puts('false')
end
这些看起来可能是一样的。实际上,示例 1 将打印 ‘false’ ,而示例 2 将打印 true。这完全是因为 or
比 ||
优先级低的事实。因此,示例 1 中的测试是:如果 1 等于 3 [false] 并且(要么 2 等于 1 ,要么 3 等于 3)[true]。由于这两个必要的条件中有一个是 false,所以整个测试返回 false。
现在来看示例 2,其测试是:(如果 1 等于 3 ,并且 2 等于 1)[false],或者 3 等于 3 [true]。这次,我们仅需要两个测试中一个成功即可;第二个测试判断为 true,所以整个测试返回 true 。
在这样的测试中,运算符优先级的副作用可能会导致非常模糊的错误。你可以通过使用括号来清楚的表达测试的含义来避免这些错误。在这里,我重写了上面的示例 1 和 2;在每种情况下,添加一对括号都会反转测试返回的布尔值:
# Example 1 (b) – now returns true
if ((1==3) and (2==1)) || (3==3) then
puts('true')
else
puts('false')
end
# Example 2 (b) – now returns false
if (1==3) and ((2==1) or (3==3)) then
puts('true')
else
puts('false')
end
否定
否定运算符 !
可以在表达式的开头使用,或者你可以在一个表达的左侧和右侧中间使用 !=
(不等于)运算符:
!(1==1) #=> false
1 != 1 #=> false
或者,你可以用 not
代替 !
:
not(1==1)
布尔运算中的怪象
请注意,Ruby 的布尔(boolean)运算符有时会以一种奇怪且不可预测的方式运行。例如:
puts( (not( 1==1 )) ) # This is ok
puts( not( 1==1 ) ) # This is a syntax error
puts( true && true && !(true) ) # This is ok
puts( true && true and !(true) ) # This is a syntax error
puts( ((true) and (true)) ) # This is ok
puts( true && true ) # This is ok
puts( true and true ) # This is a syntax error
在多数情况下,可以通过统一使用同一类型的运算符(要么用 and
,or
,not
,要么用 &&
,||
,!
)来避免这些问题,而不是混合地使用两者。另外,推荐经常使用括号。
Catch 与 Throw
Ruby 提供了一对方法 catch
和 throw
,可用于在满足某些条件时中断(break)代码块的执行。这是 Ruby 中与其它一些编程语言中的 goto
最接近的等价语法。该代码块必须以 catch
后跟一个符号(symbol)(即以冒号开头的唯一标识符)开头,例如 :done
或 :finished
。代码块本身可以用大括号限定,也可以用关键字 do
和 end
限定,如下所示:
# think of this as a block called :done
catch(:done){
# some code here
}
# and this is a block called :finished
catch(:finished) do
# some code here
end
在块内,你可以使用一个符号(symbol)作为参数调用 throw
。通常,当满足某些特定条件时,你将可以调用 throw
来跳过块中的所有剩余的未执行代码。例如,让我们假设该块包含这样一些代码,提示用户输入一个数字,用某个值来除以该数字,然后继续对结果进行大量其它的复杂计算。显然,如果用户输入 0,则后面的计算都不能完成,因此你可以通过跳出块来跳过这些计算,并继续执行块后的任何代码。这是这样做的一种方式:
catch(:finished) do
print('Enter a number: ')
num = gets().chomp.to_i
if num == 0 then
throw :finished # if num is 0, jump out of the block
end
# Here there may be hundreds of lines of
# calculations based on the value of num
# if num is 0 this code will be skipped
end
# the throw method causes execution to
# jump to here – outside of the block
puts("Finished")
实际上,你可以在块外面调用 throw
,像这样:
def dothings( aNum )
i = 0
while true
puts("I'm doing things...")
i += 1
throw(:go_for_tea) if (i == aNum)
# throws to end of go_to_tea block
end
end
catch(:go_for_tea) { # this is the :go_to_tea block
dothings(5)
}
并且你可以将 catch
块嵌套在其它的 catch
块中,像这样:
catch(:finished) do
print('Enter a number: ')
num = gets().chomp.to_i
if num == 0 then throw :finished end
puts( 100 / num )
catch(:go_for_tea) {
dothings(5)
}
puts("Things have all been done. Time for tea!")
end
与其它编程语言中的 gotos
和 jumps 一样,在 Ruby 应该非常谨慎地使用 catch
和 throw
,因为它们会破坏代码的逻辑,并且可能会引入难以发现的错误。