Day one
方法时通过向对象发送消息来唤起调用的。
而ruby里,确定绝对值的能力是内建在数字中——处理细节在内部实现中。只要发送abs消息到一个数字对象,让它去得到绝对值即可。
number = number.abs
result变量不必声明;当赋值给它的时候,它便存在了
ruby里面不用{}包含方法体,它使用end来包含
调用参数可以放到括号里 也可以不放在括号里 看个人喜好
但是除了最简单的情况下,请使用括号。
ruby对双引号字符串有更多的处理。首先,它寻找以反斜线开始的序列,并用二进制值替换它们。其中最常见的是\n,他会被回车换行符替换掉。
“”中的#{表达式}会被表达式的值替换掉。
ruby方法的返回值是最后一个计算的结果
局部变量、方法参数和方法名称都必须以小写字母或下划线开始。全局变量都有美元符号$为前缀,而实力变量也@符号开始。类变量以@@符号开始。类名称、模块名称和常量都以一个大写字母开始。
[img]http://dl.iteye.com/upload/attachment/0083/4243/65377b39-1b83-3f85-9eb1-43178134e7b9.jpg[/img]
Day Two
%w可以创建字符串数组,比普通的创建优点是不用写""了。
散列:
散列的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例子:
produces:
Start of method
In the block
In the block
End of method
同理可以为Block传递参数:
原来下面的都是Block
属性的读取:
可以简写为:
attr_reader其实是Ruby的一个方法,而:name等符号是其参数。它会通过代码求解动态地在Song类中加入势力方法体。
构成体:artist是一个表达式,它返回对于artist的一个Symbol对象。你可以将:artist看做是变量artist的名字,而普通的artist是变量的值。
写方法:
定义类方法:
创建数组:
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方法让你可以遍历收集的所有成员以累计出一个值。
读取数据流:
Blocks可以作为闭包
注意&的参数,当调用该方法的时候,Ruby会寻找一个block。block将会被转化成Proc类的一个对象,并赋值给参数。我们可以Proc#call方法去调用相应的block。
block上下文也会得到,self的值、作用域内的方法、变量和常熟。例子:
该例子使用了lambda方法,该方法将一个block转换成了Proc对象。
正则表达式:
正则表达式是Regexp类型的对象。可以通过显示地调用构造函数活使用字面量形式/pattern/和%r{pattern}来创建它们
一旦有了正则表达式对象,可以使用Regexp#match(string)或匹配操作符=~(肯定匹配)和!~(否定匹配)对字符串进行匹配。
定义一个方法:
表示查询的方法名通常以?结尾。危险的或者会修改接受者对象的方法,可以用!结尾,可以赋值的方法以一个等号=结尾。只有?!=这几个怪异的字符能够作为方法名的后缀。
方法名的参数,我们的惯例是有参数用括号 没有参数不用括号
可以给方法设置默认值:
可变长度的参数列表
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
将block从if else中抽取出来:
重构之后的代码,就是抽出一个代码快:
如果方法的最后一个参数前有一个&符号,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循环一直执行给定的块或者知道你跳出循环。
break不解释,next不解释同continue。redo从循环头重新执行循环,但不重计算循环条件表达式或者获得迭代的下一个元素。
可以传递一个值给break和next。在传统的循环中,这可能只对break有意义,此时break将设置循环的返回值。传递给next的人一直会被丢弃掉。
[color=red]while until 和 for 循环内建在Ruby语言中,但没有引入新的作用域:前面已经存在的局部变量可以在循环中使用。而循环中新创建的局部变量也可以在循环后使用。
被迭代器使用的block(比如loop和each)与此略有不同。通常,在这些block中创建的局部变量无法在block外访问。[/color]
在一个begin/end块中,使用一个或者多个rescue余怒告诉Ruby希望处理的异常类型。
当异常被引发时,Ruby将相关Exception对象的引用放在全局变量$!中,这与任何随后的异常处理不相干。
[img]http://dl.iteye.com/upload/attachment/0083/5431/4487ce82-7f17-3d6a-bb9c-6964a021ede8.jpg[/img]
关闭和删除文件后,我们可以不带任何参数来调用raise,它会重新引发$!中的异常。这是一个有用的技术,它允许我们先编写代码过滤掉一些异常,再把不能处理的异常传递到更高的层次。这几乎就像实现了一个错误处理的继承层次结构。
在begin块中可以有多个rescue子句,每个rescue子句可以指示捕获多个异常。在rescue子句的结束处,你可以提供一个Ruby的局部变量名来接受匹配的异常。
如果编写一个不带参数表的rescue子句,它的默认参数是StandardError
ensure相当于java里的finally:
尽管不那么有用,else子句是一个类似ensure子句的构造。如果存在的话,它会存现在rescue子句之后和任何一个ensure子句之前。else子句的程序体,只有当主代码没有引发任何异常时才会被执行。
引发异常:
raise
raise "bad mp3 encoding"
raise InterfaceException, "Keyboard failure", caller
第一种形式只是简单滴重复引发当前异常(如果没有当前异常的话,引发RuntimeError)。这种形式用于首先截获异常再将其继续传递的异常处理方法中。
第二种形式创建新的RuntimeError异常,把它的消息设置为指定的字符串。然后异常随着调用栈向上引发。
第三种形式使用第一个参数创建异常,然后把相关联的消息设置给第二个参数,同时吧栈信息设置给第三个参数。通常,第一个参数是Exception层次结构中某个类的名称,或者是某个异常类的对象实例引用。通常使用Kernel.caller方法产生栈信息。
典型的一个例子:
创建自己的异常:
使用方法:
尽管raise和rescue的异常机制对程序出错时终止执行已经够用了,但是如果在正常处理期间能够从一些深度嵌套的结构中跳转出来,则是很棒的。
catch定义了以给定名称为标签的block。这个block会正常执行知道遇到throw为止。
当Ruby碰到throw,它迅速回溯调用栈,用匹配的符号寻找catch代码块。当发现它之后,Ruby将栈清退到这个位置并终止该block,所以在前面的例子中,如果输入没有包含正确格式化的行,throw会跳到相应catch代码块的结束处,不仅终止了while循环,而且跳过了歌曲列表的播放。如果调用throw时指定了可选的第二个参数,这个值会作为catch的值返回。
这个例子说明了throw没有必要出现在catch的静态作用域中。
方法时通过向对象发送消息来唤起调用的。
而ruby里,确定绝对值的能力是内建在数字中——处理细节在内部实现中。只要发送abs消息到一个数字对象,让它去得到绝对值即可。
number = number.abs
result变量不必声明;当赋值给它的时候,它便存在了
ruby里面不用{}包含方法体,它使用end来包含
调用参数可以放到括号里 也可以不放在括号里 看个人喜好
但是除了最简单的情况下,请使用括号。
ruby对双引号字符串有更多的处理。首先,它寻找以反斜线开始的序列,并用二进制值替换它们。其中最常见的是\n,他会被回车换行符替换掉。
“”中的#{表达式}会被表达式的值替换掉。
ruby方法的返回值是最后一个计算的结果
局部变量、方法参数和方法名称都必须以小写字母或下划线开始。全局变量都有美元符号$为前缀,而实力变量也@符号开始。类变量以@@符号开始。类名称、模块名称和常量都以一个大写字母开始。
[img]http://dl.iteye.com/upload/attachment/0083/4243/65377b39-1b83-3f85-9eb1-43178134e7b9.jpg[/img]
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
[color=red]while until 和 for 循环内建在Ruby语言中,但没有引入新的作用域:前面已经存在的局部变量可以在循环中使用。而循环中新创建的局部变量也可以在循环后使用。
被迭代器使用的block(比如loop和each)与此略有不同。通常,在这些block中创建的局部变量无法在block外访问。[/color]
在一个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对象的引用放在全局变量$!中,这与任何随后的异常处理不相干。
[img]http://dl.iteye.com/upload/attachment/0083/5431/4487ce82-7f17-3d6a-bb9c-6964a021ede8.jpg[/img]
关闭和删除文件后,我们可以不带任何参数来调用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的静态作用域中。