Ruby系列学习资料(二)

4 、操作符和优先级

现在我们知道很多普通的数据类型,现在们看一下Ruby 的操作符。这儿按优先级高低排列它们:

1.
作用域Scope ::
2.
索引 []
3.
求幂 **
4.
一元 / etc. + - ! ~
5.
, etc. * / %
6.
/ + -
7.
逻辑移位, etc. << >>
8.
比特 and &
9.
比特 or, xor | ^
10.
比较 > >= < <=
11.
相等, etc. == === <=> != =~ !~
12.
布尔 and &&
13.
布尔 or ||
14.
范围操作符 .. ...
15.
赋值 = (also +=, -=, *=, etc.)
16.
三元 decision ?:
17.
布尔否定 not
18.
布尔 and, or and or
有些操作符不止用于一个目的;例如,操作<< 是比特左移但也是一个附加操作( 用于数组,字符串等等) ,并且也用于一个集成字符串( 多行字符串的面值) 。同样,加符号(+) 可用于加和字符串连接。你稍后会看到,很多操作符只是对方法名字的缩写。
现在我们已定义了多数数据类型和操作符。在继续之前,让我们看个例子程序。


5 、一个例子程序

在指南中,第一个程序总是"Hello, World!" ,但是让我们以稍微高级的来开始。这个小程序在华氏和摄氏之间转换:
print "Please enter a temperature and scale (C or F): "
str = gets
exit if not str or not str[0]


str.chomp!
temp, scale = str.split(" ")
if temp !~ /-?[0-9]+/

print temp, " is not a valid number.n"

exit 1
end
temp = temp.to_f


case scale

when "C", "c"

f = 1.8*temp + 32

when "F", "f"

c = (5.0/9.0)*(temp-32)

else

print "Must specify C or F.n"

exit 2
end
if c != nil then

print "#{ c}
degrees Cn"
else

print "#{ f}
degrees Fn"
end


这儿是这个程序运行的一些例子。程序显示可以从华氏转换到摄氏,从摄氏转换到华氏和处理无效的输入数字:
Please enter a temperature and scale (C or F): 98.6 F
37.0 degrees C
Please enter a temperature and scale (C or F): 100 C
212.0 degrees F
Please enter a temperature and scale (C or F): 92 G
Must specify C or F.
Please enter a temperature and scale (C or F): junk F
junk is not a valid number.
现在,像程序的技术工人,我们以print 语句开始,它实际上调用一个预定义函数print ,来写到标准输出。随后,我们调用gets( 从标准输入获取字符串) ,赋值给str
注意任何显然" 独立的" 函数调用如print gets 实际上是各种预先定义的类或对象的方法。同样,chomp 是由str 做为接收者调用的方法。Ruby 内的方法调用通常可以忽略圆括号;例如,print "foo" print("foo") 是一样的。
变量str 持有一个字符字符串,但没有理由它不可以持有其它的类型来替代。Ruby 中,数据有类型但变量没有。
特殊的方法调用exit 将中止程序。在同一行上的控制结构是个if 修饰符。If 语句在大多数语言中都有,但很落后;它若在动作的后面,则不准许有else ,也不请求关闭。至于条件,我们可以检查两件事:str 是否有值,和它是否是非空值?在文件结束情况下,我们将持有第一个条件;在没有前导数据的新换行符情况下,持有第二个条件。
这些测试工作的理由是一个未定义的变量持有一个nil 值,在Ruby nil 被计算为false 。事实上,nil false 被计算为false ,其它任何东西被计算为true 。特别地,空字符串"" 不会被计算为false ,像在其它语言内一样。
下一个语句在字符串上完成一个chomp! 操作( 移除尾部的新换行符字符) 。感叹号像个前缀用于警告这个操作实际上会修改它的接收者的值,而不只是返回一个值。感叹号被用于很多这样的实例上,以提醒程序这个方法有副作用或者是它比没有标注的副本更危险。例如,方法chomp 将返回同样结果但是不修改它的接收者。
例子的下一个语句是多重赋值。Split 方法分离字符串到值的数组中,使用空格做为定界符。左边的两个实体将被分别赋予右边的结果值。
If 语句后面使用了一个简单的正则表达式,以确定数字是否有效;如果字符串匹配包含一个可选的后有一或多个数字的减号的模式失败,它是一个无效数字并且程序退出。注意if 语句是由键盘结束终止的;尽管这儿不需要,我们应该在end 前有个else 子句。关键字then 可选的;这个语句没有使用then ,但下面却有一个。至于输出,回忆一下变量temp 也可以被植入到字符串内。
to_f 方法用于将字符串转换为一个浮点数。我们实际上将这个浮点数赋值给temp ,它原先持有的是一个字符串。
case 语句中有三种选择:用户指定C ,指定F ,或指定无效度量的情况。在第一,二个实例中,可完成计算;第三个,我们打印错误并退出。
顺便说一下,Ruby case 语句比这儿例子显示的更通用。它没有数量类型的限制,可任意使用表达式,甚至可以使用范围或正则表达式。
关于计算没有什么神奇的。但是,考虑一下变量c f case 的第一个分支。在Ruby 中没有这样的声明;当被赋值时,变量才会存在。这意味着当我们遍历case 语句失败时,这些变量只有一个会有值。
我们使用这来确定分枝被跟随的事实,以便我们可以为每个实例创建一个稍有区别的输出。C nil 比较可有效地测试c 是否有一个值。我们这儿这样做只是为了显示它可以这样做;明显地如果我们愿意,case 内的两个不同的print 语句将被使用。
你可能已经注意到我们这儿只使用了局部变量。这可能有点乱,因为它们的作用域确实覆盖了整个程序。这儿发生的是,变量对程序的顶层是局部的。变量以" 全局" 出现,因为这个例子程序内没有低层的上下文环境;然而,如果我们声明了类和方法,这些顶层变量将不可以在它们内部访问。


6 、循环和分枝

让我们花些时间看看控制结构。我们已经看到简单的if 语句和if 修饰符;也有基于关键字unless 的相应结构 ( 它也有一个可选的else) if unless 也有表达式导向的形式。我们总结如下:
if x < 5 then

statement1
end
unless x >= 5 then

statement1
end
if x < 5 then

statement1
else

statement2
end
unless x < 5 then

statement2
else

statement1
end
statement1 if y == 3
statement1 unless y != 3
x = if a>0 then b else c end
x = unless a<=0 then c else b end


在总结中,if unless 形式的行为是完全一样的。注意关键字then 可以被忽略除了最后一种情况( 表达式导向的) 。也要注意修饰符形式不能有else 子句。
Ruby 内的case 语句比大多数语言的更强大。这个多分枝甚至能测试不同的相等条件例如,一个匹配模式。由case 语句完成的测试相当于关系操作(===) 它有从一个对象到另一个对象变化的行。让我们看个例子:
case "This is a character string."

when "some value"

print "Branch 1n"

when "some other value"

print "Branch 2n"

when
/char/

print "Branch 3n"

else

print "Branch 4n"
end
这个代码将打印分枝3 。为什么?首先它试图对字符串"some value" "some other value" 进行表达式测试,以检查等同性。这失败了,国些它继续。第三个测试是对测试表达式内的模式进行。模式在这里,所以测试成功并且完成第三个prinf 语句。Else 子句总是在前面测试没有成功的缺省情况处理。
如果测试表达式是个整数,被比较的值可能是个整数范围 ( 例如, 3..8) 。在这种情况下,表达式将对范围内的每个成员进行测试。在所有实例中,第一个成功的分枝将被获取。
至于循环机制,Ruby 有一丰富的装置。While until 控制结构是两个预先测试循环,两者你期望那样工作:首先为循环指定一个延续的条件,其它的条件是终止条件。它们像if unless 一样也有修饰符形式。Kernel 模块内也有loop 方法( 缺省是无限循环) ,和与各种类相关联的迭代器( 稍后描述)
下面例子假设有称为list 的数组,它的定义是这样的:
list = %w[alpha bravo charlie delta echo]
它们遍历数组并写出每个元素:
# Loop 1 (while)i=0
while i < list.size do

print "#{ list }
"

i += 1
end
# Loop 2 (until)i=0
until i == list.size do

print "#{ list}
"

i += 1
end
# Loop 3 (post-test while)i=0
begin

print "#{ list}
"

i += 1
end while i < list.size
# Loop 4 (post-test until)i=0
begin

print "#{ list}
"

i += 1
end until i == list.size
# Loop 5 (for)for x in list do

print "#{ x}
"
end
# Loop 6 ('each' iterator)list.each do |x|

print "#{ x}
"
end
# Loop 7 ('loop' method)i=0
n=list.size-1
loop do

print "#{ list}
"

i += 1

break if i > n
end
# Loop 8 ('loop' method)i=0
n=list.size-1
loop do

print "#{ list}
"

i += 1

break unless i <= n
end
# Loop 9 ('times' iterator)n=list.size
n.times do |i|

print "#{ list}
"
end
# Loop 10 ('upto' iterator)n=list.size-1
0.upto(n) do |i|

print "#{ list}
"
end
# Loop 11 (for)n=list.size-1
for i in 0..n do

print "#{ list}
"
end
# Loop 12 ('each_index')list.each_index do |x|

print "#{ list[x]}
"
end

让我们仔细地检查下这些例子。Loops12whileuntil循环的标准形式;它们的行为本质上是一样的,但它们的条件是彼此否定的。Loops 3 4是相同的预先测试版本;测试的完成是在循环的尾部而不开始。注意在这个上下文内beginend的使用是严格的,真正发生的是跟随在begin/end(被用于异常处理)后面的whileuntil修饰符。然而,对于真正想要post-test循环的人,这儿的效果是一样的。

Loops 5 6是否是写这个循环合适的方式还有争论。注意这两个简单地互相比较。这儿没有明确的初始化,没有明确的测试或增量。这是因为数组知道它自己的尺寸, 标准的迭代器each(loop 6)自动地处理这些细节。的确,loop 5只不过是对同样迭代器的间接引用,因为for循环对有迭代器each定义的任何对象工作。For循环只是对each调用的速记;这个速记被频繁地调用,因为它为其它语法形式提供了更方法的选择。
Loops 7 8 两者使用了loop构造;像早先提到的,loop看起来像个引进一个控制结构的关键字,但它是Kernel内的方法,不是控制结构。
Loops 9 10利用了有数字索引的数组的优势;times迭代器将执行指定的次数,upto迭代器将它的参数传给指定值。这两者真的适合这种情况。
Loop 11 是在明确的,使用了一个范围的,索引值上操作的for循环,loop 12同样使用each_index迭代器在数组list的索引上遍历。
在先前的例子中,我们没有足够重视
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值