前面几章已涉及Array,Hash,Proc三种Ruby提供的类型。这章我们会涉及:numbers,strings,ranges及正则表达式。
6.1 数值类型
Ruby支持integers(整数),floating-point浮点型),rational(有理数),complex numbers(复数)。整型可是任意长度(内存空间的长度),超出正常整型的部分存储类型是Bignum的实例,不超出部分是Fixnum的实例,这种转换Ruby自动转换.
#当超出正常范围后,自动使用Bignum num = 10001 4.times do puts "#{num.class}: #{num}" num *= num end produces: Fixnum: 10001 Fixnum: 100020001 Fixnum: 10004000600040001 Bignum: 100080028005600700056002800080001
前缀标识几进制表示:0-八进制 0d-十进制 0x-十六进制 0b-二进制
123456 => 123456 # Fixnum 0d123456 => 123456 # Fixnum 123_456 => 123456 # Fixnum - underscore ignored -543 => -543 # Fixnum - negative number 0xaabb => 43707 # Fixnum - hexadecimal 0377 => 255 # Fixnum - octal -0b10_1010 => -42 # Fixnum - binary (negated) 123_456_789_123_456_789 => 123456789123456789 # Bignum #下划线被约定为.的转换,所以自动去掉。
数字串如果包含(decimal point)小数点或(exponent)指数,则将被转换为Float对象。
1.0e3不可以省去.后的1.e3,因为这样会去调用1对象的e3方法。所认.后面必需跟一个数值。
有理数是两个整数的比,复数也有实数与虚数。这两个都不能用一个串的样式表示,Ruby提供Rational与Complex两个类,调用它们的构造方法,即获取对象。
Rational(3, 4) * Rational(2, 3) # => (1/2) Rational("3/4") * Rational("2/3") # => (1/2) Complex(1, 2) * Complex(3, 4) # => (-5+10i) Complex("1+2i") * Complex("3+4i") # => (-5+10i)
所有的数值类型都是对象,提供了多个方法,比如num.abs就是获取它的绝对值。
#字符串类型不会自动转换为整型 some_file.each do |line| v1,v2 = line.split print Integer(v1) + Integer(v2), " " #这里要显示的转换 end produces: 7 11 15
How Numbers Interact(数值的内部原理)
操作符两边如果类型不一样,结果会采用更通用的那个类型来保存,比如混合整型与浮点型,则结果是浮点型,混合浮点型与复数,结果是复数。
除的规则也是一样,只是两个整型相除,结果还是整型:
1.0 / 2 # => 0.5 1 / 2.0 # => 0.5 1 / 2 # => 0
如果引入了mathn模块,则可以使用22/7这种有理数结构表示。
循环数值类型
#类Numberic支持许多迭代方法,比如: 3.times { print "X "} 1.upto(5) {|i| print i, " "} 99.downto(95) {|i| print i, " "} 50.step(80, 5) {|i print i, " "|} produces: X X X 1 2 3 4 5 99 98 97 96 95 50 55 60 65 70 75 80
迭代器如果没有跟{},则返回的是Enumerator对象。比如:
10.downto(7).with_index {|num, index| puts "#{index}: #{num}"} produces: 0: 10 1: 9 2: 8 3: 7
6.2 strings
#与JS的引号规则一致 'escape using "\\"' # => escape using "\" 'That\'s right' # => That's right
#{expr}表示希望被替代的值,如果expr是全局变量,类变量或实例变量,可以忽略。
# expr可以是表达式 "Seconds/day: #{24*60*60}" # => Seconds/day: 86400 "#{'Ho! '*3}Merry Christmas!" # => Ho! Ho! Ho! Merry Christmas! "Safe level is #$SAFE" # => Safe level is 0 #字符与数值相乘,则表示重复多少次 #$SAFE被表示为了0
插入串不仅可以是表达式,还可以是一个或多个代码段:
puts "now is #{ def the(a) 'the' + a end the('time') } for all bad coders..." produces: now is the time for all bad coders...
还可使用%q %Q here标注来构造字符串。
%q/general single-quoted string/ # => general single-quoted string %Q!general double-quoted string! # => general double-quoted string %Q{Seconds/day: #{24*60*60}} # => Seconds/day: 86400 q与Q也是可以省略的 %!general double-quoted string! # => general double-quoted string %{Seconds/day: #{24*60*60}} # => Seconds/day: 86400 #%q相当于单引号,%Q相当于双引号。 #q与Q后面跟分割符,如果是[ { (,则结束符是] } ),否则结束符是相同的符号。
---------------using a here document-----------
#通过END_OF_STRING标识为终结符,注意终结符应该放在第一列 string = <<END_OF_STRING The body of the string is the input lines up to one starting with the same text that followed the '<<' END_OF_STRING #这个要放在第一列
如果<<后面添加一个-,就可以不用遵循放在第一列的规则
string = <<-END_OF_STRING The body of the string is the input lines up to one starting with the same text that followed the '<<' END_OF_STRING
也可以有多个here document
print <<-STRING1, <<-STRING2 Concat STRING1 enate STRING2 produces: Concat enate
注意:空格不会自动trip掉
Strings And Encodings
1.9默认的是US-ASCII,2.0默认是UTF-8
#打印字符编码 plain_string = "dog" puts RUBY_VERSION puts "Encoding of #{plain_string.inspect} is #{plain_string.encoding}"
Working with Strings
下面的示例描述使用String的方法。假设有这样一个数据文件:
ut_stdtypes/songdata song’s duration, the artist, and the title /jazz/j00132.mp3 | 3:45 | Fats Waller | Ain't Misbehavin' /jazz/j00319.mp3 | 2:58 | Louis Armstrong | Wonderful World /bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red
假设需求有三:
- 将第一行数据填充到对应的属性中
- 将以mm:ss格式的时间转换为秒
- 将属性内容的空格trip掉
#下面示例演示上面需求实现的代码 #String#chomp用于去掉串后的分离符如\r\n #String#squeeze用于去掉重复的字符 !在本串操作? Song = Struct.new(:title, :name, :length) File.open("songdata") do |song_file| songs = [] song_file.each do |line| file, length, name, title = line.chomp.split(/\s*\|\s*/) name.squeeze!(" ") mins, secs = length.scan(/\d+/) songs << Song.new(title, name, mins.to_i*60 + secs.to_i) end puts songs[1] end
6.3 Ranges(范围)
三个主要用途:sequences(序列),conditions(条件),intervals(区间)
Ranges as Sequences
1..10 'a'..'z' 0...'cat'.length #..包含开始与结束;...不包含结束(高位) #转换range,调用to_a转换为数组,to_enum转换为Enumerator (1..10).to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ('bar'..'bat').to_a # => ["bar", "bas", "bat"] enum = ('bar'..'bat').to_enum enum.next # => "bar" enum.next # => "bas" ############ #Range提供方法迭代它的内容,或者测试这些内容 digits = 0..9 #定义一个Range digits.include?(5) # => true digits.max # => 9 digits.reject {|i| i < 5 } # => [5, 6, 7, 8, 9] digits.inject(:+) # => 45
我们可以让自己定义的类支持Range的这种sequence用途,所要做的是实现succ方法返回下一个对象,以及<=>方法,这个类似于compare接口。
class PowerOfTwo attr_reader :value def initialize(value) @value = value end def <=>(other) @value <=>other.value end def succ PowerOfTwo.new(@value + @value) end def to_s @value.to_s end end p1 = PowerOfTwo.new(4) p2 = PowerOfTwo.new(32) puts (p1..p2).to_a produces: 4 8 16 32
Ranges as Conditions
#这个示例用于读取第一行包含start,最后一行包含end while line=gets puts line if line=~/start/..line=~/end/ end
Ranges as Intervals
测试元素是在指定的区间。使用===操作符
(1..10) === 5 # => true (1..10) === 15 # => false (1..10) === 3.14159 # => true ('a'..'j') === 'c' # => true ('a'..'j') === 'z' # => false
#假设输入的是9.5 car_age = gets.to_f case car_age when 0...1 puts "Mmm.. new car smell" when 1...3 puts "Nice and new" when 3...10 puts "Reliable but slightly dinged" when 10...30 puts "Clunker" else puts "Vintage gem" end produces: Reliable but slightly dinged