ruby中的链式访问和方法嵌套
先看一道题,这道题是codewars上的一道题,我很早就看到了,但是不会写。等到又看到这道题的时候,我刚看完元编程那本书,觉得是可以搞定它的时候了。废话不多说,先看这道题,题目最开始是为JavaScript写的,但是也放在了ruby语言里面,这个没有关系。题目内容是有一个类Calc,通过链式方法调用,可以实现加减乘除。如图给的四个例子。数字只有0-9,运算只有加减乘除,而且每个运算只有一个操作符。(可以先不看下面,自己先想一下怎么写)
首先,每一个例子都是同样的结构---类名和四个方法。拿第一个例子来举例,ruby中调用一般都是 对象.方法的形式。那么初步的思路就是Calc类构建一个new方法(当然调用new的时候会自动调用initialize方法,如果只是返回一个实例对象,不用写这个方法),为Calc.new对象构建一个one方法,为Calc.new.one对象构建一个plus方法,为Calc.new.one.plus对象构建一个two方法。
这样用单例方法好像没有什么问题,虽然可能会复杂一些,但是应该是能做出来的。那怎么写呢?(其实我也不知道,要是有思路求指教)
我的思路是类似下面的,请看代码
class A
def one
def self.plus
def self.one
p "one+plus+one"
end
self
end
self
end
end
现在的问题就是把方法名用变量来代替,但是这样的话参数怎么传进去呢?这就是个问题了。于是这种想法就搁浅了。
关于方法嵌套定义的问题,参见http://blog.csdn.net/kiwi_coder/article/details/8122085,讲的很清楚。
一种方法不通就是另一种方法了。在上一种方法中,由于我忘记返回值为self。因此经常出现nil没有方法的错误。于是就想到了method_missing。ruby中的method_missing就是在这个对象没有某个方法的时候,会到method_missing中去找解决方案。method_missing方法正好是把方法名当成参数,于是就可以直接调用了。
代码如下:
class Calc
# Implement here
3 @@str=""
4 @@time = 0
5 def method_missing(name)
6 has = { :one => 1,:two=>2,:three => 3,:four=>4,:five => 5,
7 :six=>6,:seven => 7,:eight=>8,:nine => 9,:zero=>0,} 9 mth ={:plus=>"+",:minus=>"-",:times=>"*",:divided_by=>"/"}
10 @@str = "" if @@time == 3
11 @@str << has[name].to_s if has.has_key?(name)
12 @@str << mth[name] if mth.has_key?(name)
13 @@time += 1
14 @@time -= 3 if @@time > 3
15 if @@time == 3
16 eval @@str
17 else
18 self
19 end
20 end
end
把方法名传入method_missing的时候,先定义两个hash,然后把方法名对应的值写到一个类变量字符串@@str中,最后用eval执行字符串。@@time是用来计算方法个数,每进行一个运算,字符串清空。值得注意的是:如果不执行的时候,要返回self。
这段代码是我提交的代码,仅仅是完成了功能,但是写的不好。
不足之处:
1使用了eval,这个方法是各种书中不推荐的
2其实两个hash可以合并,这个问题不大。
3我的这个方法只是把方法名合并成字符串,有点投机取巧,而且不能适合更多的运算。
提交之后,看了别人的代码。拿出一个推荐最多的和大家分享。
代码如下:
# Chainable:
# Calc.new.one.plus.one.plus.one == 3 class Fixnum
def plus; Calc.new("+", self) end
def minus; Calc.new("-", self) end
def times; Calc.new("*", self) end
def divided_by; Calc.new("/", self) end
end class Calc
def initialize(*arguments)
if arguments.length == 2
@operation = arguments[0]
@number = arguments[1]
end
end %w(zero one two three four five six seven eight nine).each_with_index do |w,i|
define_method(w) { perform i }
end def perform number
if @operation
@number.send(@operation, number)
else
number
end
end
end
简单分析一下,4-9行在Fixnum类中定义了加减乘除,并且返回Calc的对象,带着两个参数。12-17行是初始化过程。19-21行定义了0-9的对应方法,每个方法内容都是执行perform方法。23-29行定义了perform方法。拿Calc.new.one.plus.two来解释,Calc.new,不带参数,所以返回Calc的一个实例对象。Calc.new.one,调用实例方法one,
执行perform 1,此时@operation没有值,因此返回number的值1。Calc.new.one.plus,1是Fixnum类的实例,调用plus方法,返回了一个Calc.new的对象,并且带有两个参数。因此给@operation和@number分别赋值为'+',1(这里的self就是1)。Calc.new对象又调用two方法,此时有了@operation,因此执行if条件语句里的内容,得到结果3。
这种方法还可以执行更长的方法,例如Calc.new.one.plus.two.minus.three。
总结一下:链式访问要把每一个方法的对象都弄清楚,每一个方法的返回值是下一个方法的对象。
ruby中的链式访问和方法嵌套的更多相关文章
- JavaScript中的链式调用
链模式 链模式是一种链式调用的方式,准确来说不属于通常定义的设计模式范畴,但链式调用是一种非常有用的代码构建技巧. 描述 链式调用在JavaScript语言中很常见,如jQuery.Promise等, ...
- 模拟jquery链式访问
一直写代码写代码,博客都快荒废了,眼看一月要过完,不能不留下点记忆,嘿嘿,刚研究了下jquery的链式访问,这么好用的技能我赶紧get了下,研究后略微修改,模拟一个简单的链式访问,下面这段代码支持修改 ...
- 编程中的链式调用:Scala示例
编程中的链式调用与Linux Shell 中的管道类似.Linux Shell 中的管道 ,会将管道连接的上一个程序的结果, 传递给管道连接的下一个程序作为参数进行处理,依次串联起N个实用程序形成流水 ...
- Java 中的链式编程
前言 在写项目的时候,有一个实体类有好多个属性,new 出来之后需要不停的使用setXXX( )方法,效率低而且代码可读性差,查询了下发现可以实现实体类的链式编程. public class Us ...
- 由表单验证说起,关于在C#中尝试链式编程的实践
在web开发中必不可少的会遇到表单验证的问题,为避免数据在写入到数据库时出现异常,一般比较安全的做法是前端会先做一次验证,通过后把数据提交到后端再验证一次,因为仅仅靠前端验证是不安全的,有太多的htt ...
- 如何在Objective-C中实现链式语法
在接触到开源项目 Masonry 后,里面的布局约束的链式写法让我颇感兴趣,就像下面这样: 1 2 3 4 5 6 7 8 UIEdgeInsets padding = UIEdgeInsetsMak ...
- java中的链式编程
听到链式编程听陌生的,但是写出来就感觉其实很熟悉 package test; public class Test { String name; String phone; String mail; S ...
- 如何在Objective-C中实现链式语法?
在接触到开源项目 Masonry 后,里面的布局约束的链式写法让我颇感兴趣,就像下面这样: 1 2 3 4 5 6 7 8 UIEdgeInsets padding = UIEdgeInsetsMak ...
- java开发中的链式思维 —— 设计一个链式过滤器
概述 最近在弄阿里云的sls日志服务,该服务提供了一个搜索接口,可根据各种运算.逻辑等表达式搜出想要的内容.具体语法可见https://help.aliyun.com/document_detail/ ...
随机推荐
- ssh免密码登录的几个注意事项
1, authorized_keys文件中每个公钥占一行,不能分成多行. 2,文件夹默认权限为600 3,如果遇到奇怪的问题,可以把.ssh/文件全部删掉,重新用ssh-keygen生成.
- TortoiseGit 提交代码每次需要输入用户名和密码?
每次用TortoiseGit Pull或者Push的时候都会弹出让输入用户名.密码的框, 很麻烦 ,解决办法如下: 解决办法如下: Right click → TortoiseGit → Settin ...
- 如何在ChemDraw中输入℃温度符号
化学反应常常对于温度是有一定要求的,所以用ChemDraw化学绘图工具在绘制化学反应的时候常常会用到℃温度符号.但是一些才接触ChemDraw的用户朋友不知道怎么输入℃.针对这种情况本教程来给大家分享 ...
- nib文件的默认搜索规则
if you do not specify a nib name, and do not override the loadView method in your custom subclass, t ...
- python3----requests
import requests def get_html_text(url): try: r = requests.get(url, timeout=30) r.raise_for_status() ...
- Ubuntu 编译安装搭配LNMP 环境
这里用Nginx1.2.0+mysql5.6.33+php5.6.2搭配安装环境 ---------------------------------------------Nginx BEGIN--- ...
- THINKPHP5获取设置缓存的例子
在THINKPHP5中 缓存的配置放在了config.php文件中 代码如下 如何设置缓存? 可以使用静态方法 Cache::set('key',$value,3600);//存储缓存 Cache:: ...
- mysql数据的导入与导出
参考:http://blog.sina.com.cn/s/blog_81b2b2a1010188q0.html http://blog.csdn.net/xin_yu_xin/article/det ...
- 第一个MapReduce的例子
第一个MapReduce的例子 Hadoop Guide的第一个MapReduce的例子是处理气象数据的(数据来源ncdc),终于跑通了.总结一下步骤,安装hadoop不在本文中介绍 1 数据预处理 ...
- HDU_5527_Too Rich
Too Rich Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total ...