方法时通过向对象发送消息来唤起调用的。
而ruby里,确定绝对值的能力是内建在数字中——处理细节在内部实现中。只要发送abs消息到一个数字对象,让它去得到绝对值即可。
number = number.abs
result变量不必声明;当赋值给它的时候,它便存在了
ruby里面不用{}包含方法体,它使用end来包含
调用参数可以放到括号里 也可以不放在括号里 看个人喜好
但是除了最简单的情况下,请使用括号。
ruby对双引号字符串有更多的处理。首先,它寻找以反斜线开始的序列,并用二进制值替换它们。其中最常见的是\n,他会被回车换行符替换掉。
“”中的#{表达式}会被表达式的值替换掉。
ruby方法的返回值是最后一个计算的结果
局部变量、方法参数和方法名称都必须以小写字母或下划线开始。全局变量都有美元符号$为前缀,而实力变量也@符号开始。类变量以@@符号开始。类名称、模块名称和常量都以一个大写字母开始。
Day Two
%w可以创建字符串数组,比普通的创建优点是不用写""了。
a = %w[ant bee cat] a[0] -> "ant"
散列:
inst_section = { 'cello' => 'string', 'clarinet' => 'woodwind' }
散列的key不能一样 inst_section['cello'] -> 'string'
没有key的时候返回nil,在if判断里面nil代表false,如果你想让返回一个默认值的时候可以这样写:
histogram = Hash.new(0)
这样默认返回的就是0
如果if和while程序体只有一个表达式,Ruby的语句修饰符是一种有用的快捷方式。
puts "test" if radiation > 3000
正则表达式:
if line =~ /Perl|Python/
puts "Scripting language mentioned: #{line}"
end
替换:
line.sub(/Perl/, 'ruby') 替换第一个Perl
line.gsub(/Perl/, 'ruby') 替换所有
Block例子:
def call_block puts "Start of method" yield yield puts "End of method" end call_block { puts "In the block" }
produces:
Start of method
In the block
In the block
End of method
同理可以为Block传递参数:
def call_block yield("hello", 99) end call_block {|str, num| ... }
原来下面的都是Block
[ 'cat', 'dog', 'horse' ].each {|name| print name, " " } 5.times { print "*" } 3.upto(6) {|i| print i } ('a'..'e').each {|char| print char }
属性的读取:
class Song def name @name end def artist @artist end def duration @duration end end song = Song.new("Bicylops", "Fleck", 260) song.artist ! "Fleck" song.name ! "Bicylops" song.duration ! 260
可以简写为:
class Song attr_reader :name, :artist, :duration end
attr_reader其实是Ruby的一个方法,而:name等符号是其参数。它会通过代码求解动态地在Song类中加入势力方法体。
构成体:artist是一个表达式,它返回对于artist的一个Symbol对象。你可以将:artist看做是变量artist的名字,而普通的artist是变量的值。
写方法:
class Song attr_writer :duration end
定义类方法:
class Example def instance_method # instance method end def Example.class_method # class method end end
创建数组:
a = [1,2,3]
或者
a = Array.new
使用负数索引,就是从后往前找
a = [1,2,3,4,5,6,7,8,9]
a[-1] -> 9
[start, count]
a[1,3] -> [2,3,4]
使用range索引,start end 不是个数
a[1..4] -> [2,3,4,5]
3个.代表不包含后面的那个
散列:
h = {'dog' => 'canine', 'cat' => 'feline'}
collect它所做的就是连续访问收集的所有元素
[1,3,4,5,6].collect {|x| x = x+1} -> [2,4,5,6,7]
inject方法让你可以遍历收集的所有成员以累计出一个值。
[1,3,5,7].inject(0) {|sum, element| sum+element} → 16 [1,3,5,7].inject(1) {|product, element| product*element} → 105
读取数据流:
File.open_and_process("testfile", "r") do |file| while line = file.gets puts line end end
Blocks可以作为闭包
songlist = SongList.new class JukeboxButton < Button def initialize(label, &action) super(label) @action = action end def button_pressed @action.call(self) end end start_button = JukeboxButton.new("Start") { songlist.start } pause_button = JukeboxButton.new("Pause") { songlist.pause }
注意&的参数,当调用该方法的时候,Ruby会寻找一个block。block将会被转化成Proc类的一个对象,并赋值给参数。我们可以Proc#call方法去调用相应的block。
block上下文也会得到,self的值、作用域内的方法、变量和常熟。例子:
该例子使用了lambda方法,该方法将一个block转换成了Proc对象。
def n_times(thing) return lambda {|n| thing * n } end p1 = n_times(23) p1.call(3) → 69 p1.call(4) → 92 p2 = n_times("Hello ") p2.call(3) → "Hello Hello Hello "
正则表达式:
正则表达式是Regexp类型的对象。可以通过显示地调用构造函数活使用字面量形式/pattern/和%r{pattern}来创建它们
a = Regexp.new('^\s*[a-z]') b = /^\s*[a-z]/ c = %r{^\s*[a-z]} → /^\s*[a-z]/ → /^\s*[a-z]/ → /^\s*[a-z]/
一旦有了正则表达式对象,可以使用Regexp#match(string)或匹配操作符=~(肯定匹配)和!~(否定匹配)对字符串进行匹配。
name = "Fats Waller" name=~/a/ → 1 name =~ /z/ → nil /a/=~name → 1
定义一个方法:
表示查询的方法名通常以?结尾。危险的或者会修改接受者对象的方法,可以用!结尾,可以赋值的方法以一个等号=结尾。只有?!=这几个怪异的字符能够作为方法名的后缀。
方法名的参数,我们的惯例是有参数用括号 没有参数不用括号
可以给方法设置默认值:
def cool_dude(arg1="Miles", arg2="Coltrane", arg3="Roach") "#{arg1}, #{arg2}, #{arg3}." end cool_dude -> "Miles, Coltrane, Roach." cool_dude("Bart") -> "Bart, Coltrane, Roach." cool_dude("Bart", "Elwood") -> "Bart", "Elwood, Roach." cool_dude("Bart", "Elwood", "Linus") → "Bart, Elwood, Linus."
可变长度的参数列表
def varargs(arg1, *rest)
 "Got #{arg1} and #{rest.join(', ')}"
end
varargs("one")
varargs("one","two") → "Got one and two"
varargs "one","two", "three" → "Got one and two, three"
第一个参数赋值给方法的第一个参数。不过,第二个形参的前缀为星号,因此所有剩余的参数被装入到一个新的array中,然后赋值给第二个形参。
如果你给return多个参数,方法就会将它们以数组的形式返回。你可以使用并行赋值来收集返回值。
num, square = meh_three
def five(a, b, c, d, e) "I was passed #{a} #{b} #{c} #{d} #{e}" end five(1,2,3,4,5) five(1, 2, 3, *['a', 'b']) five(*(10..14).to_a) → "Iwaspassed12345" → "I was passed 1 2 3 a b" → "Iwaspassed1011121314"
将block从if else中抽取出来:
print "(t)imes or (p)lus: " times = gets print "number: " number = Integer(gets) if times =~ /^t/ puts((1..10).collect {|n| n*number }.join(", ")) else puts((1..10).collect {|n| n+number }.join(", ")) end
重构之后的代码,就是抽出一个代码快:
print "(t)imes or (p)lus: " times = gets print "number: " number = Integer(gets) if times =~ /^t/ calc = lambda {|n| n*number } else calc = lambda {|n| n+number } end puts((1..10).collect(&calc).join(", "))
如果方法的最后一个参数前有一个&符号,Ruby将认为它是一个Proc对象。它会将其从参数列表中删除,并将Proc对象转换为一个block,然后关联到该方法。
并行赋值:
交换两个值:
a, b = b, a
Ruby的赋值实际是以并行方式执行的,所以赋值语句右边的值不受赋值语句本书的影响,在左边的任意一个变量活属性被赋值之前,右边的值按它们出现的顺序被计算出来。
数字0不被解释为假值,长度为0的字符串也不是假值。
3.times do
print "a"
end
0.upto(9) do |x|
print x, " "
end
步长为3:
0.step(12, 3) {|x| print x, " " }
File.open("ordinal").grep(/d$/) do |line| puts line
end
取出符合条件的行
内建的loop的迭代器:
loop do
end
loop循环一直执行给定的块或者知道你跳出循环。
songlist.each do |song| song.play end
break不解释,next不解释同continue。redo从循环头重新执行循环,但不重计算循环条件表达式或者获得迭代的下一个元素。
可以传递一个值给break和next。在传统的循环中,这可能只对break有意义,此时break将设置循环的返回值。传递给next的人一直会被丢弃掉。
result = while line = gets break(line) if line =~ /answer/ end process_answer(result) if result
while until 和 for 循环内建在Ruby语言中,但没有引入新的作用域:前面已经存在的局部变量可以在循环中使用。而循环中新创建的局部变量也可以在循环后使用。
被迭代器使用的block(比如loop和each)与此略有不同。通常,在这些block中创建的局部变量无法在block外访问。
在一个begin/end块中,使用一个或者多个rescue余怒告诉Ruby希望处理的异常类型。
op_file = File.open(opfile_name, "w") begin # Exceptions raised by this code will # be caught by the following rescue clause while data = socket.read(512) op_file.write(data) end rescue SystemCallError $stderr.print "IO failed: " + $! op_file.close File.delete(opfile_name) raise end
当异常被引发时,Ruby将相关Exception对象的引用放在全局变量$!中,这与任何随后的异常处理不相干。
关闭和删除文件后,我们可以不带任何参数来调用raise,它会重新引发$!中的异常。这是一个有用的技术,它允许我们先编写代码过滤掉一些异常,再把不能处理的异常传递到更高的层次。这几乎就像实现了一个错误处理的继承层次结构。
在begin块中可以有多个rescue子句,每个rescue子句可以指示捕获多个异常。在rescue子句的结束处,你可以提供一个Ruby的局部变量名来接受匹配的异常。
begin eval string rescue SyntaxError, NameError => boom print "String doesn't compile: " + boom rescue StandardError => bang print "Error running script: " + bang end
如果编写一个不带参数表的rescue子句,它的默认参数是StandardError
ensure相当于java里的finally:
f = File.open("testfile") begin # .. process rescue # .. handle error ensure f.close unless f.nil? end
尽管不那么有用,else子句是一个类似ensure子句的构造。如果存在的话,它会存现在rescue子句之后和任何一个ensure子句之前。else子句的程序体,只有当主代码没有引发任何异常时才会被执行。
f = File.open("testfile") begin # .. process rescue # .. handle error else puts "Congratulations no errors!" ensure f.close unless f.nil? end
引发异常:
raise
raise "bad mp3 encoding"
raise InterfaceException, "Keyboard failure", caller
第一种形式只是简单滴重复引发当前异常(如果没有当前异常的话,引发RuntimeError)。这种形式用于首先截获异常再将其继续传递的异常处理方法中。
第二种形式创建新的RuntimeError异常,把它的消息设置为指定的字符串。然后异常随着调用栈向上引发。
第三种形式使用第一个参数创建异常,然后把相关联的消息设置给第二个参数,同时吧栈信息设置给第三个参数。通常,第一个参数是Exception层次结构中某个类的名称,或者是某个异常类的对象实例引用。通常使用Kernel.caller方法产生栈信息。
典型的一个例子:
raise raise "Missing name" if name.nil? if i >= names.size raise IndexError, "#{i} >= size (#{names.size})" end raise ArgumentError, "Name too big", caller
创建自己的异常:
class RetryException < RuntimeError attr :ok_to_retry def initialize(ok_to_retry) @ok_to_retry = ok_to_retry end end
使用方法:
def read_data(socket) data = socket.read(512) if data.nil? raise RetryException.new(true), "transient read error" end # .. normal processing end begin stuff = read_data(socket) # .. process stuff rescue RetryException => detail retry if detail.ok_to_retry raise end
尽管raise和rescue的异常机制对程序出错时终止执行已经够用了,但是如果在正常处理期间能够从一些深度嵌套的结构中跳转出来,则是很棒的。
catch (:done) do while line = gets throw :done unless fields = line.split(/\t/) songlist.add(Song.new(*fields)) end songlist.play end
catch定义了以给定名称为标签的block。这个block会正常执行知道遇到throw为止。
当Ruby碰到throw,它迅速回溯调用栈,用匹配的符号寻找catch代码块。当发现它之后,Ruby将栈清退到这个位置并终止该block,所以在前面的例子中,如果输入没有包含正确格式化的行,throw会跳到相应catch代码块的结束处,不仅终止了while循环,而且跳过了歌曲列表的播放。如果调用throw时指定了可选的第二个参数,这个值会作为catch的值返回。
def prompt_and_get(prompt) print prompt res = readline.chomp throw :quit_requested if res == "!" res end catch :quit_requested do name = prompt_and_get("Name: ") age = prompt_and_get("Age: ") sex = prompt_and_get("Sex: ") # .. # process information end
这个例子说明了throw没有必要出现在catch的静态作用域中。