吃了大力丸的Ruby 2

來源:互聯網
上載者:User
Why's Lucky Stiff  上看來的。俺只是搬運工。能讀原文的老大們不用往下看了。

嗯,假如一個類裡有個執行個體方法。我們希望這個方法只運行一次。”切,我還以為是搶雞蛋呢“,熟讀鐵撬書的老大們開始嗤之以鼻,”不就是第391頁裡Tadayoshi Funaba的once嗎?就連上一篇《吃了大力丸的Ruby》也有類似的實現”:

01: def once(*ids)
02: for id in ids
03: module_eval <<- "end;"
04: alias_method :__#{id.to_i}__, :#{id.to_s}
05: private :__#{id.to_i}__
06: def #{id.to_s}(*args, &block)
07: (@__#{id.to_i}__ ||= [__#{id.to_i}__(*args, &block)])[0]
08: end
09: end;
10: end
11: end
 

那有沒有其它的方法,繞開這種meta-programming的常見技巧呢?於是__Why老大提出了下面這個方法:

01: class Trial
02: def run_me
03: def self.run_me; raise Exception, "NO MORE." end
04: puts "Your trial period has ended."
05: end
06: end
07:
08: t = Trial.new
09: t.run_me
10: #=> Your trial period has ended.
11: t.run_me
12: #=> (trial):3:in `run_me': NO MORE. (Exception)

注意03行。在方法run_me()裡,我們重新定義了run_me()。因為定義函數也是動態執行,當run_me()第一次運行時,03行被執行,導致新的run_me()被定義。接著04行被執行,列印出"Your trial period has ended"。函數run_me()執行完畢後,新定義取代老定義,再執行就得到第12行的結果了。注意03行用了self.run_me。這樣run_me()在instance的範疇內被改寫,所以我們可以不斷建立新的Trail執行個體,裡面的run_me()可以運行至少一次。如果去掉self,run_me()會在class層級被重寫。導致run_me()只能被第一個Trial的執行個體執行一次。

從上面我們還可以印證一個細節:Ruby裡的def..定義的方法的scope和執行這個定義的方法無關。執行定義的方法和被定義的方法的scope只取決於它們的context. 這點和JavaScript或Python都不一樣。下面一段Ruby代碼可為例證:

01: def foo
02: def bar
03: puts "bar"
04: end
05: puts "foo"
06: end
07:
08: bar
09: #=>NameError: undefined local variable or method `bar'
10: foo
11: #=>foo
12: bar
13: #=>bar



這個技術自然可以應用到記憶最佳化上:

01: class Hit
02:    def initialize(ip)
03:     @ip = ip
04:    end
05:   def country
06:      def self.country; @country end
07:     @country = `geoiplookup #{@ip}`.chomp.gsub(/^GeoIP Country Edition: /,"")
08:    end
09: end

後來有人評論道,把被記憶的資料轉換成accessor速度更快。我一般對這種語言層級的最佳化不感冒。沒有測量的情況下,再怎麼也輪不到這種局部微調啊。不過考慮到Ruby現在的速度真的有點慢(那個soap4r簡直要把人氣死),還是值得提一下:
1:def country
2:   class << self ; attr_reader:countryend
3:     @country = `geoiplookup #{@ip}`.
4:     chomp.gsub(/^GeoIP Country Edition: /,"")
5: end

為什麼用attr_reader就快呢?因為產生的attr_reader被放在了包含它的類的解析樹之外。用它時省去了動態尋找的時間:

01: class X
02: attr_reader :x
03: def y; @y; end
04: end
05: [[:class,
06: :X,
07: :Object,
08: [:defn, :x, [:ivar, :@x]],
09: [:defn, :y, [:scope, [:block, [:args], [:ivar, :@y]]]]]]

運用自我更新的方法,還可以實現無需對象的諸如有限自動機或狀態模式。是的,我沒有胡言亂語。不用對象,一樣可以實現狀態模式。具體的實現和背後的理論,就留待下一片大力丸了。性急的老大們可以到這裡看例子。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.