Ruby 板


LINE

快快乐乐学 Ruby, 平平安安... 第二话,直闯 Dice 实作 在第一话中,我们已经把最基本的 2.roll 实作出来了, 那麽我们的 Dice 需要哪些资讯?再简单也不过了,就是骰数与 面数。於是我们可以这样做: 2.dice.roll, 跟上面的 2.roll 完全是一样的,只是 2.dice 时产生了一个 Dice 物件,其中 储存了这次的骰数与面数。先来看骰子怎麽做: (btw, 我被 windows 和 unix 不同的断行字元烦死了 Orz) class Dice attr_reader :amounts, :faces # 这一行看似单纯简单,其实是伟大的 meta-programming... # 有鉴於此篇文章不是讲解高阶实作技巧,故在此只简单说明 # 这一行的功用。简单地说,就是替你实现 amounts 和 faces 的 # getter. 也就是说,这一行等同於以下 code: # def amounts # @amounts # end # def faces # @faces # end # 其中 @ 记号表示这个变数是 instance variable, # 也就是 Java 口中的 field, C++ 口中的 data member # Ruby 用各种前缀「符号」来表示各种不同的变数… # 其实这很容易造成初学者的混乱,不过久了就可以习惯了 def initialize(amounts, faces = 20) # 这个是 Ruby 的 c'tor @amounts = amounts # 由於 Ruby 变数不需宣告,所以通常 @faces = faces # 所有的 instance variable 都是定义在 # c'tor 中,再来各处都可以直接取用 # 我们的 c'tor 有两个参数,一个接受骰数,一个接受面数 # 和 Numeric 一样的是,不说明面数则表示 20 面 # 顺带一提,我对於 Ruby 使用「initialize」这个字眼感到愤怒 =.= # 理由是,这个字可以排上我错字排行榜前三名,一天到晚打错… # 一堆 i 就不说了,c'tor 名字这麽长,烦死人了… # 而且拼错字他当然不会告诉你你拼错了…他只会找不到 c'tor 而已 # 还是 D 语言的 this 方便…换做 Ruby 应该叫 self end def roll @amounts.roll(@faces) # 由於我们已经记得骰数和面数, # 所以直接 delegate 给骰数就好啦 end def min @amounts # 这个东西严格来说是额外的, # 不过在测试程式正确性时会需要用到 # composite pattern 时也会发挥功效 end def max @amounts * @faces # 最小最大值的运算应该没问题吧? end end 於是我们已经有基本的骰子能用了,2.roll, 2.dice.roll, 2.dice.min => 2 2.dice.max => 2*20 => 40 嗯,不知道有没有读者已经发现了,其实现在 Numeric 根本就没有 dice 这个 method... 2.dice 的执行本身会错误 我不会承认是我忘了说,事实上是因为 2.dice 需要看到 Dice 的 实作,所以我把 2.dice 这种东西移到这里再讲。记得 Ruby 的 class 可以随时打开来扩充对吧?现在我们就来扩充: class Numeric def dice(faces = 20) Dice.new(self, faces) end end 也就是说,其实 2.dice 只是 Dice.new(2) 的简单呼叫法而已。 同样你也可以写: Dice.new(2).roll, 只是比较罗唆而已。 ok, 再来就是最复杂的 DiceSet 了,不过说是复杂其实也没很复杂, 只是比起上面几样东西是稍微复杂了一点。 class DiceSet attr_reader :min, :max # 这个东西记得吧?由於 composite pattern 的缘故, # 我们很难去记得 DiceSet 的正确骰数和面数(其实只是我懒得做而已) # (有兴趣的读者可以把这个当作练习题…) # 所以现在这里我只简单记忆 min 和 max 是多少,而不是去计算 def initialize(*args) # 於是我们看到另外一个变数符号了,前缀 * # 这绝对不是指标的意思,在参数列中,这表示不定长度的引数 # 就有点像 C 中的 ... 参数一样,Java 1.5 我记得也有加上这个功能, # 但怎麽用我已经忘了 XD # 总之,Ruby interpreter 会将引数打包成一个 Array 丢给 args, # 所以事实上 args 是一个 Array, 储存着所有的引数 # p.s. 引数是 argument, 参数是 parameter # 前者是呼叫端(caller)所传进来的东西, # 後者是接收端(callee)所宣示要吃的东西 @diceset = args # 事实上,DiceSet 就只是一个 Array 的包装(wrapper) # 我们在 DiceSet 内部中储存一群的 Dice, 和或 DiceSet # 当我们叫 DiceSet 掷骰时,就是把这一群的 Dice, 和或 DiceSet # 掷出结果,然後把这些结果全部加起来,再回传出去 # 我们的参数/引数 args, 正是一开始传进来的所有骰子/骰子组 # 日後要再加骰子/骰子组到这个骰子组中的话,就只要加到这个 Array 中即可 @min = 0 # 初始化为零 @max = 0 # 初始化为零 @diceset.each do |i| @min += i.min; @max += i.max; end # 开始计算真实的 min 和 max 是多少? # 当然这非常简单,就是所有的骰子/骰子组的 min 和 max 合 # 所以只要环岛一周(travel)并取得各观光景点(骰子/骰子组)的 # min 和 max 合就 ok 了 end def roll result = 0 # 初始化总合 @diceset.each { |i| result += i.roll } # 将每个骰子/骰子组的丢掷结果加进总合中 result # 传回 end def <<(dice) # 把骰子/骰子组加入这个骰子组 # 有点 append 或 insert 的意味在 @diceset << dice # delegate 给我们内部的 Array, 加入骰子/骰子组 @min += dice.min # 最大最小不要忘记加上去 @max += dice.max self # 把自己传回去…其实没有什麽特殊用意, # 只是 Ruby 的最後一行一定会传出去, # 但传个 @max 出去好像怪怪的是吧? end end ok, 全部的实作就是这样了 这样一行一行看的缺点在於,丧失整体性,只知道 how 不知道 why, 所以现在就来说明一下 composite pattern... 在 static typing 的系统中,要实作 composite pattern 势必得 需要让两个物间中有着继承关系,像是这样: p.s. composite pattern 的意思是,让群体等同於个体 也就是说不管你呼叫的对象是谁,是个体就让他做事, 是群体就让那一群一起去做事 C++: class Componenet{}; class Composition: public Component{}; Java: class Component{} class Composition extends Component{} 旅行的部份就可以这样做: in Composition: // 虚拟码 void doSomething(){ for each Component i in components{ i.doSomething(); } } 由於 Composition 也是一种 Component, 所以 Composition 也可以加到上文的 components 中。如果我们呼叫到真正的 Component, 则这个 Component 做事,但如果其实他是 Composition 呢? 他会再去呼叫他底下的 components 做事,於是形成某种形式的递回呼叫 要做到这点,就必须让 Compositon 和 Component 有着继承关系, 否则编译器会大大抱怨,你让不会做事的人做事,有问题! 继承是个威力非常强大的能力,同时也让程式复杂性大大提昇, 所以在任何时候都该谨记可以不用继承时,不要用继承,继承应当视为 最後的秘密武器…。这不是在说实作 composite pattern 不应该使用继承, 而是在 static typeing 的系统中,只有这个办法能实作 composite pattern. 但由於 Ruby 是 dynamic typing 的系统,又是所谓 duck-typing, 也就是说只要有共同名称的方法(method),就可以呼叫,不需要管 他真实的型别到底是什麽。只要会就去做,不管你是不是其中一份子。 这让程式在实作时产生极大的弹性。在 static typing 的系统中, 如果程式原本就没有设计 composite pattern, 忽然要改成这种设计 是有一点麻烦的,因为牵涉到了继承体系。但在 dynamic typing 中, 要实作 composite pattern, 只要把名字取得一模一样就可以了。 也就是说,要後来再挂上 Composition 这个类别,是很容易的, 只要把方法名字取得跟 Component 一模一样就可以。 在这里,就是 min, max, 还有 roll. 所以在 DiceSet 中,我们可以将 DiceSet 也视为一种 Dice, 直接加到 diceset 的 Array 中,需要时直接呼叫他就可以了。 这样看起来,dynamic typing 似乎占有极大的优势,不过其实这也只是 一种取舍而已。dynamic typing 将编译期的成本移动到执行期, 成本没有消失,只是转换和移动而已。static typing 的成本是 programmer 需要下比较多的心力去维护制作,dynamic typing 将这成本变成了电脑 执行期间对资源的索取程度。一个是由 programmer 事先告诉电脑「他」 会不会做事,另一个则是电脑在需要执行时,才去问「骰子组」会不会 做「骰子」会做的事情?所以在执行效率上,可能跟 static typing 有着好几个数量级的差异。 回顾一下 DiceSet 用法吧: dice_set = DiceSet.new(2.dice, 3.dice(4), 8.dice(d)) dice_set.roll # 2~40 + 3~12 + 8~32 共 13~84 another_dice_set = DiceSet.new(2.dice, dice_set) another_dice_set.roll # 2~40 + 13~84 共 15~124 another_dice_set << 2.dice another_dice_set.roll # 15~124 + 2~40 共 17~164 another_dice_set << DiceSet.new(dice_set, dice_set, 2.dice) another_dice_set.roll # 17~164 + 13~84 + 13~84 + 2~40 共 35~368 不过写到这里我有点懒了,所以 DiceTest 我们下周再看吧 @_@ -- Nobody can take anything away from him. Nor can anyone give anything to him. What came from the sea, has returned to the sea. Chrono Cross --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 220.135.28.18 ※ 编辑: godfat 来自: 220.135.28.18 (08/10 18:25)
1F:推 poga:头推^^/ 08/10 18:28







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Soft_Job站内搜寻

TOP