天天看點

ruby查缺補漏

《programming ruby中文版》前3部分我并不準備細看,畢竟我接觸ruby也有一段時間了,隻準備快速地掠過一遍,查缺補漏;重點放在第3部分的核心内容上,至于第四部分的參考手冊更多作為工具書了。僅在此記錄下一些值的注意的東西。

1.全局變量$_,預設當gets方法傳回輸入的行時,同時儲存在全局變量$_,并且正規表達式如果作為條件語句(if或者while)時預設是跟這個全局變量進行比對,而print參數為空時也是列印這個全局變量。這是早期ruby向perl語言學習的結果。可以看看這個例子:

while gets

  if /ruby/

      print

  end

end

這樣的風格不值的提倡,全局變量的使用應該盡力減少,ruby也在逐漸脫離perl主義的風格

2.ruby中的單例模式:

class logger

  private_class_method:new

  @@logger=nil

  def logger.create

    @@logger=new unless @@logger

    @@logger

log1=logger.create

log2=logger.create

puts log1.object_id

puts log2.object_id

3.ruby中的block作用:

1)疊代器,通常是内部疊代器

2)事務blocks,c#的using語句倒是跟這個有點像,其實就是讓對象自身負責資源的打開和關閉,這是通過kernel.block_given?實作的,比如file.open方法,當後面跟着一個block的時候,就會自動關閉打開的檔案資源,如果不是,就需要自己處理。

3)作為閉包,與javascript和其他語言中的閉包概念一緻,一個例子:

def n_times(thing)

  return lambda {|n| thing*n}

p1=n_times(23)

puts p1.call(3)

puts p1.call(2)

通過lambda方法将一個block轉為proc對象,盡管參數thing在block被真正調用時已經離開了作用範圍,但是仍然可以使用

4.ruby中數字的最大長度取決于系統,這跟java,c#通過虛拟機規範的不同,數字類型的幾個常用疊代器:times,upto,downto,step,如:

2.step(10,2){|i| print i,' '}  =>2,4,6,8,10

5.ruby中的字元串是8位元組的序列,可以存儲可列印的字元和二進制資料。比較有趣3種建構字元串常量方式:%q(對應于單引号定義的字元串),%q(雙引号)以及here documents,比如:

s=<<end_of_string

   測試測試啦

end_of_string

6.range,書中翻譯為區間,我倒更喜歡範圍這個詞。區間的3個用途:

1)用作序列,最常見的,如1..2,a..z等,可以定義自己的區間,隻要實作succ和<=>比較方法

2)作為條件,書中的例子很經典:

while line=gets

   puts line if line=~/start/..line=~/end/

#利用全局變量簡化為,不建議這樣寫

   print if /start/../end/

3)作為間隔,看看某個值是否落入區間範圍内,使用===操作符比較

7.正規表達式,這是重頭戲。ruby中的perl風格的正規表達式,其實也是内建在ruby中的正規表達式對象的外部包裝,關鍵的就是兩個類regexp類和matchdata類。一些peri程式員熟悉的記号:

$&    比對的字元串

$`    比對前的字元串

$'    比對後的字元串

$1    第一個分組,$2,$3...類似

詳細的就不抄書了,正規表達式我在學習javascript的時候已經系統地學過,倒是不感覺吃力。

8.在方法中定義可變長度參數,隻要參數前加*号即可,java1.5也已經支援可變參數,比如object...obj。

另外,在方法中,将數組展開為參數,可以在數組前加一個*号,比如:

def three(a,b,c)

   print "this is #{a},#{b},#{c}"

three([1,2,3)]

#上面這樣調用報參數數目錯誤,正确的用法如下:

three(*[1,2,3)] =>this is 1,2,3

将hash清單直接做為參數,可能在2.0支援,目前采用的要求散列數組在正常的參數之後,并位于任何的block或者數組之前

9.ruby中的多線程:

1)ruby建立線程,見下面這個例子,開3個線程分别通路3個站點,并且對3個線程通過調用join方法,直到3個線程都結束,主線程才結束,來自書中例子:

require 'net/http'

pages=%w(www.javaeye.com www.sina.com.cn www.blogjava.net)

$proxy_addr = 'x.x.x.x'

$proxy_port = 80

threads=[]

for page_to_fetch in pages

  threads<<thread.new(page_to_fetch) do |url|

    h=net::http.proxy($proxy_addr, $proxy_port).new(url,80)

    puts "fetcing:#{url}"

    resp=h.get('/',nil)

    puts "got #{url}:#{resp.message}"

end    

threads.each{|thr| thr.join}

2)線程中如何共享變量?可以通過[]=簡單地把目前線程看成一個散清單,這裡沒有考慮同步問題:

count=0

10.times do |i|

  threads[i]=thread.new do 

    sleep(rand(0.1))

    thread.current["mycount"]=count

    count+=1

threads.each{|t| t.join;print t["mycount"],", "}

puts "count =#{count}"

3)通過設定abort_on_exception,如果是true,未處理的線程異常将殺死所有正在運作的線程,如果是false,則殺死目前運作的線程,其他線程繼續運作。修改上面的例子檢視下:

  threads[i]=thread.new(i) do |j|

    raise "boom!" if j==4 

threads.each do |t|

  begin

    t.join

    print t["mycount"],", "

  rescue runtimeerror=>e

    puts "failed:#{e.message}"

輸出(随機的):

8, 1, 6, 3, failed:boom!

2, 4, 7, 0, 5, count =9

在開頭加上:

thread.abort_on_exception=true

殺死所有的運作程序,報出異常,而不會産生輸出。

4)通過線程的一系列方法:pass,join,value,stop來進行線程的排程

5)互斥的實作,與其他語言一樣,不外乎加鎖、信号量、隊列的方式。看看加鎖是如何做的,通過monitor庫的關鍵字synchronize實作,如下面這個例子,兩個線程遞增同一個變量,似乎結果應該是20000:

#require 'monitor'

class counter#<monitor

  attr_reader:count

  def initialize

    @count=0

  #  super

  def tick

  #  synchronize do

      @count+=1

  #  end

c=counter.new

t1=thread.new{10000.times{c.tick}}

t2=thread.new{10000.times{c.tick}}

t1.join;t2.join

print c.count

很遺憾,結果不會是20000,而是比它小的一個數值,這裡的問題就是因為通路共享資源沒有進行同步的緣故,使用monitor庫,請将上面代碼中的注釋去掉,可以得到正确的結果

使用monitor,不一定要使用繼承,也可以使用mixin,甚至:

lock=monitor.new

t1=thread.new{10000.times{lock.synchronize{c.tick}}}

還可以把特定的對象放入monitor,比如:

c.extend(monitormixin)

t1=thread.new{10000.times{c.synchronize{c.tick}}}

ruby查缺補漏

.

6)條件變量和隊列的方式不準備抄書了,ruby中對線程的操作都是直接調用作業系統的指令,特别是*nix支援的非常好,可惜我對linux也是個初哥。

10.ruby中表達式很重要的一個特點是:任何表達式都有傳回值,包括指派語句、條件語句、循環語句之類。

1)ruby中對布爾表達式的規定是:任何不是nil或者常量false的值都為真

2)注意,在方法中調用通路屬性的函數,需要寫上調用者self,否則将處理為局部變量

3)defined?方法用于傳回參數的描述,如果未定義,傳回nil

4)邏輯表達式中,and和or的優先級低于&&,||

5)ruby沒有for語句,因為ruby通過内建在對象中的疊代器提供了循環通路的能力,最簡單的内建疊代器:loop do ....end

6)隻要你的類支援each方法,你就可以使用for ... in ..語句循環它

7)對循環可以使用break(打斷跳出),redo(從頭重新循環,目前疊代),next進行排程。另外,還有retry,用于完全重新開始循環

8)while,until和for循環内建到了ruby語言中,但沒有引入新的作用域:前面存在的局部變量可以在循環中使用,而循環中新建立的局部變量也可以在循環後使用。而被疊代器使用的block則不同,在block中建立的局部變量無法在block外通路。

11.ruby的異常處理

類似于java的try...catch...finnaly,ruby對應的是begin...rescue...ensure...end,将産生異常的代碼放在這個塊中進行處理。可以通過$!得到異常資訊,或者提供局部變量名,我改寫了一下我的google線上翻譯機,增加異常處理,并用exit代替break:

def translate

  txt=stdin.gets

  exit if txt.strip=='e' or txt.strip=='exit'

  temp=txt.split(' ')

  if temp[1]=='1' or temp.size==1

    langpair='en|zh-cn'

  else

    langpair='zh-cn|en'

  #使用代理  

  begin 

    $proxy_addr = 'localhost'

    $proxy_port = 80

    response = net::http.proxy($proxy_addr,$proxy_port).post_form(uri.parse("http://translate.google.com/translate_t"),{'text'=>temp[0],'langpair'=>langpair})

    response.body =~ /<div id=result_box dir=ltr>(.*)<\/div>/

  rescue  standarderror =>e

    $stderr.print "網絡錯誤:"+e

    result = $1 

    puts '翻譯内容:'+temp[0]

    puts 'google傳回:'+result

    puts '-------------------退出請打e或者exit---------------'

    translate

translate

引發一個異常使用raise語句,重新引發目前異常,如果沒有,就引發一個runtimeerror,常見使用方式:

raise interfaceexception,"keyboard failure",caller

其中的caller生成了棧的資訊。另外,catch...throw語句用于在異常發生時從深度嵌套的結構中跳轉出來。

12。關于子產品,作用有二:作為命名空間和mixin機制。子產品的mixin機制可以說是ruby的一個精華所在,通過mixin,可以變相地實作了多重繼承,并且可以動态地為類添加和删除功能。這一部分注意兩點:

1)子產品中定義的執行個體變量可能與包含子產品的類的執行個體變量産生名稱沖突。可以使用子產品一級的散清單,以目前對象的id做索引,來儲存特定于目前子產品的執行個體變量解決這個問題。比如:

module test

  state={}

  def state=(value)

    state[object_id]=value

  def state

    state[object_id]

class client

  include test

c1=client.new

c2=client.new

c1.state='a'

c2.state='b'

puts c1.state

puts c2.state

2)是關于方法的查找路徑,順序是:目前類-》類的mixin子產品-》超類-》超類的mixin,另外mixin的子產品,最後混入的同名方法将覆寫前面混入的。

13.irb的配置和指令,今天發現irb原來也是可以玩出很多花樣的。記錄些有趣的:

1)可以使用按tab鍵兩次來自動補全,要求加載irb/completaion庫。比如這樣啟動irb:

irb -r irb/completion

或者進入irb後手工require:

require 'irb/completation'

當然,還有更好的方法,呆會介紹

2)子會話,在irb中使用irb可以建立子會話,通過指令jobs可以檢視所有的子會話。建立子會話的時候指定一個對象,子會話的self将綁定該對象,比如:

irb 'test'

reverse

=>"tset"

length

=>4

self

=>"test"

irb_quit

3)在linux下可以通過配置.irbrc配置檔案來進行初始化定制,在windows環境你可以在ruby安裝目錄下的bin看到一個irb.bat檔案,通過配置檔案來定制irb,比如我們為irb增加ri和tab自動補齊功能:

@echo off

goto endofruby

#!/bin/ruby

#

#   irb.rb - intaractive ruby

#       $release version: 0.9.5 $

#       $revision: 1.2.2.1 $

#       $date: 2005/04/19 19:24:56 $

#       by keiju ishitsuka([email protected])

require "irb"

require 'irb/completion'

def ri(*names)

  system(%{ri.bat #{names.map{ |name| name.to_s}.join(" ")}})

if __file__ == $0

  irb.start(__file__)

else

  # check -e option

  if /^-e$/ =~ $0

    irb.start(__file__)

    irb.setup(__file__)

__end__

:endofruby

"%~d0%~p0ruby" -x "%~f0" %*

4)常用指令:

exit,quit,irb_exit,irb_quit——退出

conf,context,irb_context——檢視配置資訊

irb <obj>——建立子會話,如果提供obj,作為self

jobs,irb_jobs——列出irb的子會話

irb_fg,fg n——切換子會話

kill n,irb_kill n——殺死一個irb子會話

14.類的執行個體變量,類除了類變量、執行個體變量外,還有一個類的執行個體變量的概念:

class test

  #類的執行個體變量

  @cls_var = 123

  def test.inc

    @cls_var += 1

  class<<self

    attr_accessor:cls_var

test.inc

15.子類竟然可以改變父類定義方法的通路級别:

class base

  def amethod

    puts "got here"

  private :amethod

class derived1 < base

  public :amethod

class derived2 < base

  def amethod(*args)

    super

  public:amethod  

d1=derived1.new

d2=derived2.new

d1.amethod

d2.amethod

不知道ruby是基于什麼樣的考慮允許這樣的行為。

文章轉自莊周夢蝶  ,原文釋出時間5.17