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 !!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,568评论 25 707
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,014评论 11 349
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,036评论 29 470
  • 你不知道的大理和丽江~ 第三天,从大理出发,经过苍山洱海,直奔丽江,今天一天是整个行程当中最有价值的一天,大理抵达...
    樱花开了阅读 211评论 0 0
  • 闻到你的气息 我醉了,听到你的声音 我碎了,我甚至听到了 热泪浸湿枕头的声音 我笑了,一朵火红色的 玫瑰在我眼帘处...
    如是完美阅读 178评论 0 0