回想起咱門初學(xué)C跟Java語(yǔ)言的時(shí)候,或許會(huì)以為這個(gè)世界上只有這兩門語(yǔ)言。 當(dāng)時(shí)老師或者教科書肯定不是一上來就教你如何用這門語(yǔ)言去連接數(shù)據(jù)庫(kù),而是要求你用這門語(yǔ)言去實(shí)現(xiàn)一些簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),以及排序這類簡(jiǎn)單的算法。Oh,sorry,我可能讓你回憶起一些不太好的事情了。特別是后來背棄了編程這條路的同學(xué),你們一定覺得這個(gè)過程特別的枯燥,而且艱辛。
還記得那時(shí)候我們?yōu)榱藢?shí)現(xiàn)一種叫做先進(jìn)先出的被呼喚為隊(duì)列的數(shù)據(jù)結(jié)構(gòu),以及一種后進(jìn)先出的被呼喚為棧的數(shù)據(jù)結(jié)構(gòu)耗費(fèi)了我們多少時(shí)間了嗎?現(xiàn)在的你肯定不會(huì)手動(dòng)去寫這些數(shù)據(jù)結(jié)構(gòu)了,別人早就寫好的代碼很容易就能夠在網(wǎng)上找到,我們直接用便可(當(dāng)然那時(shí)候我們不知道有Github這種東西)。

后來,我決定要走一條不尋常的路(研習(xí)一門動(dòng)態(tài)語(yǔ)言)的時(shí)候。我發(fā)現(xiàn)更不用做這些事情了,所謂的棧,隊(duì)列,這些數(shù)據(jù)結(jié)構(gòu),在許多語(yǔ)言的內(nèi)置庫(kù)里面都已經(jīng)有類似的實(shí)現(xiàn)。今天,請(qǐng)?jiān)试S我用Ruby來講這個(gè)事情,另外我會(huì)結(jié)合Ruby的Forwardable模塊,優(yōu)雅地實(shí)現(xiàn)方法代理。
1. 簡(jiǎn)單模擬棧跟隊(duì)列
Ruby的內(nèi)置類 Array的存在,為我們簡(jiǎn)單操作序列型數(shù)據(jù)提供了可能,我們只需要用字面量的方式就能夠很容易地創(chuàng)建一個(gè)序列
array = [1,2,3,4]
=> [1, 2, 3, 4]
然后?我們可以基于Array的實(shí)例來簡(jiǎn)單地模擬?;蛘哧?duì)列的行為。
1)模擬棧
我只需要簡(jiǎn)單地使用Array#shift以及Array#unshift兩個(gè)實(shí)例方法就能夠很簡(jiǎn)單地模擬棧的功能
# 棧的實(shí)現(xiàn)
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]
是不是很簡(jiǎn)單?我只需要反復(fù)調(diào)用上面兩個(gè)方法,就能夠完成棧能完成的事情了。
2)模擬隊(duì)列
接下來模擬隊(duì)列,隊(duì)列也比較簡(jiǎn)單,我們只需要調(diào)用Array#push方法就可以在隊(duì)列的末尾插入一個(gè)元素。然后通過Array#shift方法,移除并獲取隊(duì)列頭部的元素。
# 隊(duì)列的實(shí)現(xiàn)
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]
但是如果在平時(shí)的業(yè)務(wù)代碼中我們給隊(duì)友們提供這樣的隊(duì)列,或者棧的話。你可能就見不到明天的太陽(yáng)了,我相信軟件行業(yè)里面爛代碼吸引的仇恨,并不亞于英雄聯(lián)盟里面的豬隊(duì)友。那我們一般會(huì)怎么做?請(qǐng)接著往下讀。
2. 定義隊(duì)列或者棧的相關(guān)類
更加語(yǔ)義化的方式是定義相關(guān)的類(Stack,Queue),然后封裝對(duì)應(yīng)的操作方法。這樣,當(dāng)別人用到我們定義好的有特定行為的類的時(shí)候心情就會(huì)更加舒坦。我們代碼可讀性也更高。我開篇一直在扯語(yǔ)言的事情,就是提醒一下在我們使用動(dòng)態(tài)語(yǔ)言的時(shí)候,請(qǐng)盡量跳出靜態(tài)語(yǔ)言的思維定勢(shì)。很多事情其實(shí)可以更簡(jiǎn)單。動(dòng)態(tài)語(yǔ)言往往更關(guān)注行為,而不是類型。
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
我知道現(xiàn)在的代碼并不是很好看,但是請(qǐng)相信我,最起碼它是可以運(yùn)行的
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]>
雖然有點(diǎn)粗糙,但最起碼它也是一個(gè)隊(duì)列。它能夠完成隊(duì)列的基本行為。
2) 高富帥的寫法
初入Ruby難免會(huì)寫出一些比較矮窮搓的代碼,沒事,隨著我們經(jīng)驗(yàn)的累積,我相信我們會(huì)慢慢寫出高富帥的代碼出來。為了讓上述的代碼更簡(jiǎn)短一些,我們可以增強(qiáng)原有類的功能。Ruby經(jīng)常會(huì)使用Module來完成增強(qiáng)任務(wù)。下面介紹一個(gè)叫做Forwardable的模塊,它提供了一些好用的類方法,可以幫我們簡(jiǎn)單地定義對(duì)應(yīng)類的一些實(shí)例方法,并且,把定義好的實(shí)例方法代理到實(shí)例變量相關(guān)的某個(gè)方法上。這樣說可能有點(diǎn)迷糊,我這里列舉一個(gè)官方文檔的例子。
require 'forwardable'
class Queue
extend Forwardable
def initialize
@q = []
end
def_delegator :@q, :push, :enq
def_delegator :@q, :shift, :deq
end
沒錯(cuò),已經(jīng)寫完了。這就是Forwardable模塊的最直接的用法,類Queue通過擴(kuò)展模塊Forwardable,就會(huì)得到Forwardable::def_delegator這樣的類方法,通過這個(gè)類方法定義實(shí)例方法Queue#enq并把功能代理到Array#push這個(gè)實(shí)例方法上。在解析環(huán)境中運(yùn)行以上代碼。得到的結(jié)果跟之前的例子是一樣的。
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]>
好吧,我也知道廢話很多,其實(shí)主要想說明兩件事情
- 有些東西用動(dòng)態(tài)語(yǔ)言來寫雖然運(yùn)行效率會(huì)稍微慢一點(diǎn),但是勝在靈活,很多時(shí)候我們可以更優(yōu)雅地去實(shí)現(xiàn)一些功能。
- Ruby以
Plugin的方式來增強(qiáng)原來的類的行為,提供一些方便我們?nèi)粘J褂玫恼Z(yǔ)法糖讓我們的代碼更精煉。*
如上面的Forwardable模塊提供了Forwardable#def_delegator類方法,讓我們可以方便地實(shí)現(xiàn)代理功能,而不需要手動(dòng)地去實(shí)現(xiàn)對(duì)應(yīng)的細(xì)節(jié)。畢竟我們都知道寫得越多錯(cuò)的越多。
3. 寫在最后
這篇文章作為技術(shù)文章似乎有點(diǎn)短,并且瞎扯淡的成分居多。我就是想黑一下Java,然后用Ruby的方式來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的隊(duì)列。主要是為了展示一下Ruby的優(yōu)雅。至于Forwardable模塊,我對(duì)它了解目前還只是停留在應(yīng)用層面。說實(shí)在的我對(duì)它的源碼很感興趣,希望后面有機(jī)會(huì)可以再寫一篇文章深入剖析它的源代碼。(好吧,我也承認(rèn)我不在這篇文章剖析它的源代碼是因?yàn)槲腋揪瓦€沒看懂它源代碼,他們寫得有點(diǎn)深?yuàn)W。)
如果您還覺得意猶未盡時(shí)間尚早,請(qǐng)用Java去實(shí)現(xiàn)相應(yīng)的隊(duì)列以及棧數(shù)據(jù)結(jié)構(gòu)吧。
