第四章基本类型§4.1 Array
Array也称作数组,是一系列元素的有序集合。你可以显式使用Array类的new方法来创建一个数组对象,你也可以用方括号包围起来一些以逗号分隔的数字或字符串构成一个数组。
irb(main):007:0> a = [ "first" "second" "third" ]
=> ["firstsecondthird"]
irb(main):008:0> a = [ "first", "second", "third" ]
=> ["first", "second", "third"]
irb(main):009:0> a.class
=> Array
irb(main):010:0> a.length
=> 3
irb(main):011:0> a[0]
=> "first"
irb(main):012:0> a[1]
=> "second"
irb(main):013:0> a[2]
=> "third"
irb(main):014:0> a[3]
=> nil
irb(main):015:0> b = Array.new
=> []
irb(main):016:0> b.class
=> Array
irb(main):017:0> b.length
=> 0
irb(main):018:0> b[0] = "first"
=> "first"
irb(main):019:0> b[1] = "second"
=> "second"
irb(main):020:0> b
=> ["first", "second"]
数组可以使用 []来索引,其实 [] 是Array类的一个方法,它甚至可以被子类覆盖(overridden)。Ruby中比较有趣的是有多种对数组的索引方法,你可以用负数来索引数组。负数表示从尾部开始,例如索引为-1表示最后一个元素,索引为-2表示倒数第二个元素,以此类推。
irb(main):021:0> a = [ 1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):022:0> a[-1]
=> 5
irb(main):023:0> a[-2]
=> 4
irb(main):024:0> a[-9]
=> nil
你也可以使用一对数来索引数组,第一个数表示开始位置,第二数表示从开始位置起的元素数目。
irb(main):025:0> a = [ 1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):026:0> a[1, 3]
=> [2, 3, 4]
irb(main):027:0> a[3, 1]
=> [4]
irb(main):028:0> a[-3, 1]
=> [3]
你甚至可以用一个范围来索引数组,..表示包含尾部元素,... 表示不包含尾部元素。
irb(main):029:0> a = [ 1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):030:0> a[1..3]
=> [2, 3, 4]
irb(main):031:0> a[1...3]
=> [2, 3]
§4.2 Hash
Hash也称作哈希表,类似于数组但是每个元素都有索引,有时候也被称作关联数组,哈希数组或字典。哈希表和数组不同,数组只能使用数字索引,而哈希表则可以使用任何对象索引。哈希表和数组的另一个显著不同是哈希表中的元素是无序的。在Ruby中每个哈希表都是Hash类的对象。
在哈希表中,我们称索引为Key,被索引的元素称为Value。
我们可以使用=>连接的元素来创建一个哈希表,注意哈希表外部是使用大括号包围。
irb(main):032:0> h = { "first" => "Amy", "second" => "Mike", "third" => "Tom" }
=> {"third"=>"Tom", "second"=>"Mike", "first"=>"Amy"}
irb(main):033:0> h.length
=> 3
irb(main):034:0> h["first"]
=> "Amy"
irb(main):035:0> h['second']
=> "Mike"
irb(main):036:0> h[100] = "Henry"
=> "Henry"
irb(main):037:0> h["nine"] = "Rose"
=> "Rose"
irb(main):038:0> h
=> {"third"=>"Tom", "second"=>"Mike", 100=>"Henry", "first"=>"Amy", "nine"=>"Rose"}
§4.3 Number
Ruby支持整数类型和浮点数类型。整数可以是任意长度(这个长度只和内存大小有关)。在一定范围内的整数被视为Fixnum类的对象。超出这个范围的整数被视为Bignum类的对象。
num = 81
6.times do
puts "#{num.class}: #{num}"
num *= num
end
运行结果:
Fixnum: 81
Fixnum: 6561
Fixnum: 43046721
Bignum: 1853020188851841
Bignum: 3433683820292512484657849089281
Bignum: 11790184577738583171520872861412518665678211592275841109096961
和C/C++相同,Ruby规定以0开头的数为八进制数,以0x开头的数为十六进制数,以0b开头的数为二进制数。
irb(main):001:0> 16
=> 16
irb(main):002:0> 020
=> 16
irb(main):003:0> 0x10
=> 16
irb(main):004:0> 0b10000
=> 16
一个数中间可以用下划线连接,下划线自动被忽略。
irb(main):005:0> 123_456_789
=> 123456789
可以使用"?\C-x"或"?\cx"生成控制字符。
如果一个数包含小数点或者包含"e",那么这个数将被转为Float类的对象。
irb(main):012:0> 1.0.class
=> Float
irb(main):013:0> 1.0e3.class
=> Float
§4.4 String
String也称作字符串,是单引号或双引号包围起来的一串字符。单引号和双引号的意义有所不同,双引号包围的字符作变量替换,单引号包围的变量不做替换。可以在字符串中使用 #{expr}嵌入代码。
irb(main):022:0> "The seconds in a day is: #{24*60*60}"
=> "The seconds in a day is: 86400"
irb(main):023:0> 'The seconds in a day is: #{24*60*60}'
=> "The seconds in a day is: \#{24*60*60}"
也可以使用 %q和 %Q 来生成字符串对象。%q相当于单引号,%Q相当于双引号。
irb(main):051:0> %q/Single quote/
=> "Single quote"
irb(main):052:0> %Q/Double quote/
=> "Double quote"
irb(main):053:0> %q/ #{50*50} /
=> " \#{50*50} "
irb(main):054:0> %Q/ #{50*50} /
=> " 2500 "
%q 和 %Q后面的第一个字符为分隔符。二哥分隔符之间的字符被认为一个是字符串。但是如果这个分隔符是 [ { <,那么结束标志为匹配的 ] } >。
irb(main):055:0> %q{This is a string}
=> "This is a string"
irb(main):056:0> %Q[This is a string]
=> "This is a string"
irb(main):057:0> %q<This is a string>
=> "This is a string"
你也可以使用“Here Document”的方法来生成字符串,这种方法规定 <<之后的字符串作为结束标志。
string = <<END_OF_STRING
With publication started in June 1948 and a current circulation of 3 million,
People's Daily is the most influential and authoritative newspaper in China.
According to UNESCO, it takes its place among the world top 10.
END_OF_STRING
需要注意,表示结尾的END_OF_STRING必须放在行首。
§4.5 Range
Range也称作范围,用来表示一个都是连续的值的序列。可以使用 ..和 ... 操作符来产生Range,前者表示包含最后一个元素,后者表示不包含最后一个元素。Range对象所属的类是Range。注意Range和Array是不同的,可以使用Range类的to_a方法将一个Range对象转化为Array对象。
irb(main):003:0> (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):004:0> ('bar'..'bat').to_a
=> ["bar", "bas", "bat"]
irb(main):005:0> (1...10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
Range除了表示一个序列外还可以出现在条件语句中。在条件语句中,可以把Range看作一个双向开关,当第一个条件满足时打开开关,当第二个条件满足时关闭开关。
a = [0, 1, 2, 3, 4, 5, 6]
a.each do |i|
print i, " " if i == 1 .. i == 5
end
执行结果为
1 2 3 4 5
上述代码中if i == 1 .. i == 5表示只有满足i==1且不满足i==5时条件为真,当不满足i==1或满足i==5时条件为假,直观地看,就是表示元素需要位于范围之中。
可以使用===来测试一个元素是否在某个范围:
irb(main):093:0> (1..10) === 3
=> true
irb(main):094:0> (1..10) === 30
=> false
irb(main):095:0> (1..10) === 2.71828
=> true
irb(main):096:0> ('a'..'f') == 'c'
=> true
irb(main):097:0> ('a'..'f') == 'g'
=> false
Range也可以位于case语句之内:
score = 98
case score
when 85..100 then puts "A"
when 70...85 then puts "B"
when 60...70 then puts "C"
else puts "D"
end
执行结果为:
A
§4.6 Symbol
Symbol是个简单的对象,它使用名字作为唯一的标识符。Symbol对象代表解释器内部一个唯一的名字。Symbol的产生很简单,只需要给一个字符序列前添加“:”或使用“to_sym”方法。 Symbol对象从属于Symbol类。
String和Symbol两者具有紧密的联系。每个symbol都有个字符串的名字(可以使用to_s方法得到)。而每个String可以请求它的相应symbol(通过to_sym方法)。String和Symbol是紧密相联的,但它们不是同一个东西,他们分别是String类和Symbol类的对象。
有读者可能会问,为什么要存在Symbol对象呢?因为symbol可以大大提高速度。Symbol的内部表示是一个整数,用来做Hash表中检索字符串的关键字,而Ruby语言执行时解析器、运算器需要大量的类名字、方法名字的检索,这可以大大加快解析和执行时字符串查找的速度。
想想,如果没有Symbol,如果需要用方法名称作为参数时,我们必须给一个字符串用来表示方法的名称,解释器处理时首先要作字符串解析,然后才能找到出相应的方法,而如果使用Symbol会大大加快这一速度。
在使用中,Symbol往往表示一个名字,例如一个变量 foo的值为1,那么 :foo可以理解为变量名,如果直接引用foo,会得到1,但如果是 :foo就指变量名本身。
Symbol对象是唯一的。每次你在代码中使用:test,你是要引用一个名字为"test"的Symbol类的对象。Ruby保证系统中只有一个名字为test的Symbol对象, 所以所有对:test的引用都将引用同一个对象。
irb(main):001:0> module One
irb(main):002:1> class Test
irb(main):003:2> end
irb(main):004:1> $f1 = :Test
irb(main):005:1> end
=> :Test
irb(main):006:0> module Two
irb(main):007:1> Test = 1
irb(main):008:1> $f2 = :Test
irb(main):009:1> end
=> :Test
irb(main):010:0> def Test()
irb(main):011:1> end
=> nil
irb(main):012:0> $f3 = :Test
=> :Test
irb(main):013:0> $1.object_id
=> 4
irb(main):014:0> $2.object_id
=> 4
irb(main):015:0> $3.object_id
=> 4
§4.7 正则表达式
正则表达式的类是Regexp,可以使用/或%r生成正则表达式。
irb(main):103:0> a = /\s*[a-f]/
=> /\s*[a-f]/
irb(main):104:0> a.class
=> Regexp
irb(main):105:0> b = %r{\s*[a-f]}
=> /\s*[a-f]/
irb(main):106:0> b.class
=> Regexp
irb(main):107:0> c = Regexp.new('\s*[a-f]')
=> /\s*[a-f]/
irb(main):108:0> c.class
=> Regexp
你可以使用Regexp#match(string)方法或者=~运算符来匹配正则表达式,你也可以使用!~来测试是否不匹配。
irb(main):113:0> sentence = "This is a dog."
=> "This is a dog."
irb(main):114:0> sentence =~ /dog/
=> 10
irb(main):115:0> sentence =~ /a/
=> 8
irb(main):116:0> /o/ =~ sentence
=> 11
irb(main):117:0> sentence !~ /xyz/
=> true
另外,在匹配正则表达式时,会将匹配到的字符串存放在 $&变量中,$' 变量中存放已经匹配过的字符序列,$` 变量中存放还未匹配的字符序列。
irb(main):118:0> sentence = "This is a dog."
=> "This is a dog."
irb(main):119:0> sentence =~ /a/
=> 8
irb(main):120:0> puts $&
a
=> nil
irb(main):121:0> puts $'
dog.
=> nil
irb(main):122:0> puts $`
This is
=> nil