如何在Ruby中正确链接自定义方法?

我正在尝试为以下两种方法执行链接方法。 运行此代码后,我不断获得以下输出:

#% 

我的问题是:在Ruby中链接方法的正确方法是什么?

这是我的代码:

 class SimpleMath def add(a,b=0) a + b return self end def subtract(a,b=0) a - b return self end end newNumber = SimpleMath.new() print newNumber.add(2,3).add(2) 

你想做这样的事吗?

 class SimpleMath def initialize @result = 0 end #1 add function def add(val) @result += val self end #2 Subtract function def subtract(val) @result -= val self end def to_s @result end end newNumber = SimpleMath.new p newNumber.add(2).add(2).subtract(1) 

对于任何数量的论点

 class SimpleMath def initialize @result = 0 end #1 add function def add(*val) @result += val.inject(&:+) self end #2 Subtract function def subtract(*val) @result -= val.inject(&:+) self end def to_s @result end end newNumber = SimpleMath.new p newNumber.add(1, 1).add(1, 1, 1, 1).subtract(1) 

让我们定义一个SimpleMath类的实例:

 sm = SimpleMath.new #=> # 

这里要注意三件事:

  • sm是一个变量。 在Ruby中,变量由小写字母表示,可选地用下划线分隔(例如, my_var )。
  • 虽然可以在new之后添加() ,当new没有参数(也就是“参数”)时,这是可选的,通常不会。
  • 如果关键字return不存在,Ruby返回该方法执行的最后一次计算。 在这里,您通常会将最后一行写为self ,并且会返回。 唉,重要的是,无论有没有关键字return ,返回self都不是你想要的。

在IRB中尝试以下内容:

 sm.add(2) #=> # 

毫无疑问,你期望它返回2+0 #=> 2 ,但它返回self ,正如你在上面看到的那样,实际上是sm# )。

您只需删除该行即可解决此问题:

 return self 

subtract

 class SimpleMath def add(a,b=0) a + b end def subtract(a,b=0) a - b end end 

现在

 sm = SimpleMath.new sm.add(2) #=> 2 

但是,如果我们尝试链接另一个add ,我们还有另一个问题:

 sm.add(2).add(2,3) #=> NoMethodError: undefined method `add' for 2:Fixnum 

这条消息非常清楚:类Fixnum ,其中2是一个实例,没有名为add实例方法。 那是因为你为SimpleMath类定义了它,而不是为Fixnum定义它。

当Ruby执行sm.add(2).add(3,4) ,它首先计算sm.add(2) #=> 2 ,这将表达式减少到2.add(3,4) 。 然后它尝试将方法add (带有两个参数)发送到2 ,但是找到类2.class #=> Fixnum没有实例方法add ; 因此例外。

我们可以通过为类Fixnum定义这些方法来纠正该错误:

 class Fixnum def add(a,b=0) a + b end def subtract(a,b=0) a - b end end 

您可以通过运行以下命令确认已将这些方法添加到Fixnum类:

 Fixnum.instance_methods.sort 

现在,另一个问题:

 sm = Fixnum.new #=> NoMethodError: undefined method `new' for Fixnum:Class 

哦,我的,类Fixnum没有new方法! 那是因为Fixnum的实例是整数,无法创建。 您可以轻松确认整数是Fixnum实例:

 72.class #=> Fixnum -3.class #=> Fixnum 

因此我们可以通过将add方法发送到任何 Fixnum实例来调用add方法:

 72.add(2) #=> 2 -3.add(2) #=> 2 

现在让我们尝试链接add操作:

 72.add(2).add(3,4) #=> 7 72.add(2000000).add(3,4) #=> 7 

没有例外,但没有链接。 解决此问题的方法是再次更改方法:

 class Fixnum def add(b=0) puts "in add, self = #{self}, b = #{b}" self + b end def subtract(b=0) puts "in subtract, self = #{self}, b = #{b}" self - b end end 

我已经在每个方法中添加了一个puts语句,以防需要更多调试。 当代码正常工作时,我们将删除它们。 我们来测试一下:

 2.add(3) #=> 5 in add, self = 2, b = 3 5.add #=> 5 in add, self = 5, b = 0 5.add(7) #=> 12 in add, self = 5, b = 7 2.add(3).add.add(7) #=> 12 in add, self = 2, b = 3 in add, self = 5, b = 0 in add, self = 5, b = 7 2.subtract(5) #=> -3 in subtract, self = 2, b = 5 -3.subtract #=> -3 in subtract, self = -3, b = 0 2.subtract(5).subtract #=> -3 in subtract, self = 2, b = 5 in subtract, self = -3, b = 0 2.add(3).subtract(5).add(7) #=> 7 in add, self = 2, b = 3 in subtract, self = 5, b = 5 in add, self = 0, b = 7 

成功! 得到它?

这个家伙( tjackiw.tumblr.com )使用这个作为面试问题,并提供了一个非常干净的解决方案,了解正确答案与以下内容类似的原因和原因:

 class Interpreter def initialize(&block) instance_eval(&block) end def at(time) @time = time self end def when(date) @date = date self end def we(*people) @people = people self end def going(where) @where = where self end def output [@people.join(' and '), "are going", @where, @date, "at", @time].join(' ') end end 

另一种方法是通过chainable_methods gem构建管道。

文章中描述

 require 'chainable_methods' module SimpleMath include ChainableMethods def add(a, b=0) a + b end def subtract(a, b=0) a - b end end SimpleMath. chain_from(5). add(5). add(5). subtract(3). unwrap