Ruby的Forwardable模塊瞎扯淡

回想起咱門初學C跟Java語言的時候,或許會以為這個世界上只有這兩門語言。 當時老師或者教科書肯定不是一上來就教你如何用這門語言去連接數據庫,而是要求你用這門語言去實現一些簡單的數據結構,以及排序這類簡單的算法。Oh,sorry,我可能讓你回憶起一些不太好的事情了。特別是后來背棄了編程這條路的同學,你們一定覺得這個過程特別的枯燥,而且艱辛。

還記得那時候我們為了實現一種叫做先進先出的被呼喚為隊列的數據結構,以及一種后進先出的被呼喚為棧的數據結構耗費了我們多少時間了嗎?現在的你肯定不會手動去寫這些數據結構了,別人早就寫好的代碼很容易就能夠在網上找到,我們直接用便可(當然那時候我們不知道有Github這種東西)。

隊列

后來,我決定要走一條不尋常的路(研習一門動態語言)的時候。我發現更不用做這些事情了,所謂的棧,隊列,這些數據結構,在許多語言的內置庫里面都已經有類似的實現。今天,請允許我用Ruby來講這個事情,另外我會結合Ruby的Forwardable模塊,優雅地實現方法代理。

1. 簡單模擬棧跟隊列

Ruby的內置類 Array的存在,為我們簡單操作序列型數據提供了可能,我們只需要用字面量的方式就能夠很容易地創建一個序列

array = [1,2,3,4]
=> [1, 2, 3, 4]

然后?我們可以基于Array的實例來簡單地模擬棧或者隊列的行為。

1)模擬棧

我只需要簡單地使用Array#shift以及Array#unshift兩個實例方法就能夠很簡單地模擬棧的功能

# 棧的實現
pry(main)> array
=> [1, 2, 3, 4]
pry(main)> array.unshift(1)
=> [1, 1, 2, 3, 4]
pry(main)> array.shift()
=> [1]
pry(main)> array
=> [1, 2, 3, 4]

是不是很簡單?我只需要反復調用上面兩個方法,就能夠完成棧能完成的事情了。

2)模擬隊列

接下來模擬隊列,隊列也比較簡單,我們只需要調用Array#push方法就可以在隊列的末尾插入一個元素。然后通過Array#shift方法,移除并獲取隊列頭部的元素。

# 隊列的實現
pry(main)> array
=> [1, 2, 3, 4]
pry(main)> array.push(1)
=> [1, 2, 3, 4, 1]
pry(main)> array.shift()
=> [1]
pry(main)> array
=> [2, 3, 4, 1]

但是如果在平時的業務代碼中我們給隊友們提供這樣的隊列,或者棧的話。你可能就見不到明天的太陽了,我相信軟件行業里面爛代碼吸引的仇恨,并不亞于英雄聯盟里面的豬隊友。那我們一般會怎么做?請接著往下讀。

2. 定義隊列或者棧的相關類

更加語義化的方式是定義相關的類(Stack,Queue),然后封裝對應的操作方法。這樣,當別人用到我們定義好的有特定行為的類的時候心情就會更加舒坦。我們代碼可讀性也更高。我開篇一直在扯語言的事情,就是提醒一下在我們使用動態語言的時候,請盡量跳出靜態語言的思維定勢。很多事情其實可以更簡單。動態語言往往更關注行為,而不是類型。

1) 屌絲方案

class Queue
  def initialize
    @q = []
  end

  def enq(*argument_list)
    @q.push(*argument_list)
  end

  def deq(*argument_list)
    @q.shift(*argument_list)
  end
end

我知道現在的代碼并不是很好看,但是請相信我,最起碼它是可以運行的

pry(main)> q = Queue.new
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[]>
pry(main)> q.enq(1,2,3,4)
=> [1, 2, 3, 4]
pry(main)> q
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[1, 2, 3, 4]>
pry(main)> q.deq()
=> 1
pry(main)> q
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[2, 3, 4]>
pry(main)> q.deq(2)
=> [2, 3]
pry(main)> q
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[4]>

雖然有點粗糙,但最起碼它也是一個隊列。它能夠完成隊列的基本行為。

2) 高富帥的寫法

初入Ruby難免會寫出一些比較矮窮搓的代碼,沒事,隨著我們經驗的累積,我相信我們會慢慢寫出高富帥的代碼出來。為了讓上述的代碼更簡短一些,我們可以增強原有類的功能。Ruby經常會使用Module來完成增強任務。下面介紹一個叫做Forwardable的模塊,它提供了一些好用的類方法,可以幫我們簡單地定義對應類的一些實例方法,并且,把定義好的實例方法代理到實例變量相關的某個方法上。這樣說可能有點迷糊,我這里列舉一個官方文檔的例子。

require 'forwardable'
class Queue
  extend Forwardable
  def initialize
    @q = []
  end

  def_delegator :@q, :push, :enq
  def_delegator :@q, :shift, :deq
end

沒錯,已經寫完了。這就是Forwardable模塊的最直接的用法,Queue通過擴展模塊Forwardable,就會得到Forwardable::def_delegator這樣的類方法,通過這個類方法定義實例方法Queue#enq并把功能代理到Array#push這個實例方法上。在解析環境中運行以上代碼。得到的結果跟之前的例子是一樣的。

pry(main)> q = Queue.new
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[]>
pry(main)> q.enq(1,2,3,4)
=> [1, 2, 3, 4]
pry(main)> q
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[1, 2, 3, 4]>
pry(main)> q.deq()
=> 1
pry(main)> q
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[2, 3, 4]>
pry(main)> q.deq(2)
=> [2, 3]
pry(main)> q
=> #<Thread::Queue:0x007ff00f0ccfe0 @q=[4]>

好吧,我也知道廢話很多,其實主要想說明兩件事情

  1. 有些東西用動態語言來寫雖然運行效率會稍微慢一點,但是勝在靈活,很多時候我們可以更優雅地去實現一些功能。
  2. Ruby以Plugin的方式來增強原來的類的行為,提供一些方便我們日常使用的語法糖讓我們的代碼更精煉。*

如上面的Forwardable模塊提供了Forwardable#def_delegator類方法,讓我們可以方便地實現代理功能,而不需要手動地去實現對應的細節。畢竟我們都知道寫得越多錯的越多。

3. 寫在最后

這篇文章作為技術文章似乎有點短,并且瞎扯淡的成分居多。我就是想黑一下Java,然后用Ruby的方式來實現一個簡單的隊列。主要是為了展示一下Ruby的優雅。至于Forwardable模塊,我對它了解目前還只是停留在應用層面。說實在的我對它的源碼很感興趣,希望后面有機會可以再寫一篇文章深入剖析它的源代碼。(好吧,我也承認我不在這篇文章剖析它的源代碼是因為我根本就還沒看懂它源代碼,他們寫得有點深奧。)

如果您還覺得意猶未盡時間尚早,請用Java去實現相應的隊列以及棧數據結構吧。

Java是最好的語言

Happy Coding and Writing !!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,532評論 25 708
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,373評論 11 349
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,217評論 30 472
  • 你不知道的大理和麗江~ 第三天,從大理出發,經過蒼山洱海,直奔麗江,今天一天是整個行程當中最有價值的一天,大理抵達...
    櫻花開了閱讀 273評論 0 0
  • 聞到你的氣息 我醉了,聽到你的聲音 我碎了,我甚至聽到了 熱淚浸濕枕頭的聲音 我笑了,一朵火紅色的 玫瑰在我眼簾處...
    如是完美閱讀 201評論 0 0