莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

          ruby查缺補漏

          Posted on 2007-04-07 10:30 dennis 閱讀(2338) 評論(0)  編輯  收藏 所屬分類: 動態語言
              《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.aygfsteel.com)
          $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}}}
          .

          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("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
            
          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是基于什么樣的考慮允許這樣的行為。













          主站蜘蛛池模板: 鄄城县| 教育| 达拉特旗| 天津市| 台南市| 南城县| 钦州市| 平武县| 喜德县| 米林县| 台安县| 昆明市| 新和县| 河西区| 永胜县| 湄潭县| 正阳县| 资源县| 江津市| 太谷县| 甘孜| 黎平县| 平阴县| 潼关县| 南漳县| 鄂尔多斯市| 长子县| 普格县| 台南市| 江永县| 喀什市| 开鲁县| 沂水县| 东兰县| 无极县| 多伦县| 清镇市| 泌阳县| 平乐县| 永泰县| 绵阳市|