閱讀544 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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
  end
end
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}
end
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/
end

#利用全局變量簡化為,不建議這樣寫
while gets
   
print if /start/../end/
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}"
end

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
end    
threads.each{
|thr| thr.join}

2)線程中如何共享變量?可以通過[]=簡單地把當前線程看成一個散列表,這裏沒有考慮同步問題:
count=0
threads
=[]
10.times do |i|
  threads[i]
=Thread.new do 
    sleep(rand(
0.1))
    Thread.current[
"mycount"]=count
    count
+=1
  end
end
threads.each{
|t| t.join;print t["mycount"],""}
puts 
"count =#{count}"

3)通過設置abort_on_exception,如果是true,未處理的線程異常將殺死所有正在運行的線程,如果是false,則殺死當前運行的線程,其他線程繼續運行。修改上麵的例子查看下:
count=0
threads
=[]
10.times do |i|
  threads[i]
=Thread.new(i) do |j|
    
raise "boom!" if j==4 
    sleep(rand(
0.1))
    Thread.current[
"mycount"]=count
    count
+=1
  end
end
threads.each do 
|t|
  begin
    t.join
    
print t["mycount"],""
  rescue RuntimeError
=>e
    puts 
"Failed:#{e.message}"
  end
end
puts 
"count =#{count}"
輸出(隨機的):
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
  end
  
def tick
  
#  synchronize do
      @count+=1
  
#  end
  end
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=Counter.new
c.extend(MonitorMixin)
t1
=Thread.new{10000.times{c.synchronize{c.tick}}}
dot.gif.

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:
require 'net/http'
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'
  end
  
#使用代理  
  begin 
    $proxy_addr 
= 'localhost'
    $proxy_port 
= 80
    response 
= Net::HTTP.Proxy($proxy_addr,$proxy_port).post_form(URI.parse("https://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
  
else
    result 
= $1 
    puts 
'翻譯內容:'+temp[0]
    puts 
'google返回:'+result
    puts 
'-------------------退出請打e或者exit---------------'
    translate
  end
end
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
  end
  
def state
    State[object_id]
  end
end
class Client
  include Test
end
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(keiju@ruby-lang.org)
#

require 
"irb"
require 
'irb/completion'
def ri(*names)
  system(
%{ri.bat #{names.map{ |name| name.to_s}.join(" ")}})
end
if __FILE__ == $0
  IRB.start(
__FILE__)
else
  
# check -e option
  if /^-e$/ =~ $0
    IRB.start(
__FILE__)
  
else
    IRB.setup(
__FILE__)
  end
end
__END__
:endofruby
"%~d0%~p0ruby" -"%~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
  end
  
class<<self
    attr_accessor:cls_var
  end
end
Test.inc
Test.inc


15.子類竟然可以改變父類定義方法的訪問級別:
class Base
  
def aMethod
    puts 
"Got here"
  end
  private :aMethod
end

class Derived1 < Base
  public :aMethod
end

class Derived2 < Base
  
def aMethod(*args)
    super
  end
  public:aMethod  
end

d1
=Derived1.new
d2
=Derived2.new
d1.aMethod
d2.aMethod


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

文章轉自莊周夢蝶  ,原文發布時間5.17

最後更新:2017-05-17 13:35:05

  上一篇:go  微軟為VS.net添加單元測試功能
  下一篇:go  org.springframework.core.styler包解讀