programming ruby

Day one

方法时通过向对象发送消息来唤起调用的。

而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的静态作用域中。

猜你喜欢

转载自mistbow.iteye.com/blog/1850588