发表于:2006.04.03 16:58
分类: Ruby Programmer 2nd
出处:http://my4java.itpub.net/post/9983/65272
---------------------------------------------------------------
第七章 表达式(4-3)
Case 表达式
Ruby的 case 表达式非常强大,就像多个if的固化物一样。它有两种风格。
第一种风格特别像多个if语句:它让你列出一些条件并执行第一个条件为true的语句。例如,闰年必须可被400除,或可被4除而不可被100除。
leap = case
when year % 400 == 0: true
when year % 100 == 0: false
else year % 4 == 0
end
第二种风格的case语句或许更常用。你在case语句的顶部指定一个目标,然后每个when子句列出一或多个比较。
case input_line
when "debug"
dump_debug_info
dump_symbols
when /ps+(w+)/
dump_variable($1)
when "quit", "exit"
exit
else
print "Illegal command: #{input_line}"
end
像if一样,case返回最后执行的语句的结果,如果你的条件和后面的语句都在一行,你也需要加一个then关键字。
kind = case year
when 1850..1889 then "Blues"
when 1890..1909 then "Ragtime"
when 1910..1929 then "
when 1930..1939 then "Swing"
when 1940..1950 then "Bebop"
else "Jazz"
end
像if语句,你也可以使用冒号:来代替then关键字。
kind = case year
when 1850..1889 : "Blues"
when 1890..1909 : "Ragtime"
when 1910..1929 : "
when 1930..1939 : "Swing"
when 1940..1950 : "Bebop"
else "Jazz"
end
case操作数用when关键字后面的比较表达式与目标(关键字case后面的表达式)进行比较。这个测试使用comparison===target。只有一个类定义了===语义(所有内建类都定义了===),那个类的对象就可以被用在case表达式中。
例如,正则表达式定义===为简单的模式匹配。
case line
when /title=(.*)/
puts "Title is #$1"
when /track=(.*)/
puts "Track is #$1"
when /artist=(.*)/
puts "Artist is #$1"
end
Ruby类是类Class的实例,它的定义===用来测试参数是不是类或它的超类的一个实例。所以(abandoning the benefits of polymorphism and bringing the gods of refactoring down around your ears), 你可以测试对象的类。
case shape
when Square, Rectangle
# ...
when Circle
# ...
when Triangle
# ...
else
# ...
end
不要告诉他人,Ruby支持原始的灵巧的内建循环结构。
while循环根据它的条件的真假来执行0次或者多次语句,比如,下面程序将一直运行,直到输入被打断。
while line = gets
# ...
end
util循环与之相反;知道条件为真,才停止操作。
until play_list.duration > 60
play_list.add(song_list.pop)
end
像if和unless一样,while和until也可以用作语句修饰句。
a = 1
a *= 2 while a < 100
a -=10 until a < 100
a => 98
在89页的布尔表达式一节中,我们说过range也可以作为布尔表达式,当一些事件发生时返回true然后保持这个true直到下一个事件发生。这个机制多用于循环中,在下面的例子中,我们从一个包含从first到tenth的数字的文本文件中读取数据,但是只打印从以third开头的行,直到遇到fifth开头的行为止。
file = File.open("ordinal")
while line = file.gets
puts(line) if line =~ /third/ .. line =~ /fifth/
end
produces:
third
fourth
fifth
你可以看到用Perl写上面例子时两者的轻微差别。
file = File.open("ordinal")
while file.gets
print if ~/third/ .. ~/fifth/
end
produces:
third
fourth
fifth
这儿有些隐藏在背后的行为:gets将读入的最后行赋值给全局变量$_,~操作符完成与$_的正则表达式的匹配操作,然后不带参数的print打印$_。这种代码是Ruby社区的公认风格。
布尔表达式中的range元素本身也可以是个表达式。它们在布尔表达式每次被计算时都会被求值。例如,下面代码使用了变量$.来包含当前输入的行数,并显示在/eig/和/nin/之间的行号一到三。
File.foreach("ordinal") do |line|
if (($. == 1) || line =~ /eig/) .. (($. == 3) || line =~ /nin/)
print line
end
end
produces:
first
second
third
eighth
ninth
这里有一点需要注意,当while和until用作语句修饰句的时候,如果它们修饰的语句以begin开头,end结尾的块,块内这段代码将总会执行,而不管后面的条件。
print "Hellon" while false
begin
print "Goodbyen"
end while false
produces:
Goodbye
如果你读了前面几节,你可能会有些灰心。”Ruby有基本的内建循环结构”,它说。不要失望,亲爱的读者,这是个好消息。Ruby不需要任何内建循环,因为所有的东西都由Ruby的迭代器实现。
例如,Ruby没有for循环,至少没有C,C++和Java的那种。代替的是,Ruby使用定义在各种内建类中定义的方法,它们提供相同的功能,而且更少有错误。
让我们看看一个例子:
3.times do
print "Ho! "
end
produces:
Ho! Ho! Ho!
这可以轻易地避免off-by-one 错误;这个循环将执行3次。除了times,整数可以调用downto,和upto在指定范围内循环,还可使用step循环所有数字。例如,传统的从0到9的循环(类似i=0; i < 10; i++)可写成下面这样:
0.upto(9) do |x|
print x, " "
end
produces:
0 1 2 3 4 5 6 7 8 9
一个从0到12,步长为3的循环如下:
0.step(12, 3) {|x| print x, " " }
produces:
0 3 6 9 12
用于数组和其它容器的迭代的each方法也可以用来循环。
[ 1, 1, 2, 3, 5 ].each {|val| print val, " " }
produces:
1 1 2 3 5
如果一个类支持了each方法,那么在模块Enumerable(文档在433页,总结在113-113页)中的方法也可以直接使用。比如,File类提供了each方法,依次返回一个文件的每一行。使用Enumerable中的grep方法,我们可以只迭代符合条件的行。
File.open("ordinal").grep(/d$/) do |line|
puts line
end
produces:
second
third
最后也是最简单的,Ruby提供了一个内建的最基本的迭代器loop。
loop {
# block ...
}
loop迭代器一直调用给定的block(或者你调用了break跳出循环,后面会讲到)。
前面我们说道Ruby支持的最基本循环是while和until,而for指的什么呢?for几乎只是一块语法糖。当你这样写时
for song in songlist
song.play
end
Ruby将会把它翻译为如下:
songlist.each do |song|
song.play
end
for循环和each形式的唯一区别是它们体内定义的局部变量的作用域。这个讨论在99页。
你可以在支持each的类上使用for方法进行迭代,比如Array或者Range。
for i in ['fee', 'fi', 'fo', 'fum']
print i, " "
end
for i in 1..3
print i, " "
end
for i in File.open("ordinal").find_all {|line| line =~ /d$/}
print i.chomp, " "
end
produces:
fee fi fo fum 1 2 3 second third
一旦你的类支持了each方法,你就可以使用for来进行遍历。
class Periods
def each
yield "Classical"
yield "Jazz"
yield "Rock"
end
end
periods = Periods.new
for genre in periods
print genre, " "
end
produces:
Classical Jazz Rock






