今天在codewars上做题时发现一道很不错的题目,写下来分享。
The goal is to implement simple calculator which uses fluent syntax:
Calc.new.one.plus.two # Should return 3
Calc.new.five.minus.six # Should return -1
Calc.new.seven.times.two # Should return 14
Calc.new.nine.divided_by.three # Should return 3
There are only four operations that are supported (plus, minus, times, divided_by) and 10 digits (zero, one, two, three, four, five, six, seven, eight, nine).
Each calculation consists of one operation only.
TestCases;
Test.assert_equals(Calc.new.four.plus.five, 9)
Test.assert_equals(Calc.new.five.plus.five,10)
分析:第一种思路是用一个类变量将三个方法存储在一个字符串里,然后调用eval方法运行字符串。用一个哈希将英文字符和阿拉伯数字及运算符对应起来,将对应的字符存储到@instance_variable里面,最后当长度为3(调用3次方法)的时候运行字符串。
class Calc
NUM = {
one: '1',two: '2',three: '3',four: '4',five: '5',six: '6',seven: '7',eight: '8',
nine: 9,zero: 0,plus: '+',minus: '-',times: '*', divided_by: '/'
}
def method_missing name
@str = "#{@str} #{NUM[name]}".strip
@str.split.size == 3 ? eval(@str) : self
end
end
第二种思路分析,一共要调用三次实例方法,第一次返回Fixnum实例对象,然后给Fixnum猴子补丁(plus,minus,times,divided_by),第二次方法调用时候返回Calc实例,同时将operation和对象本身传递进Calc对象里,然后再次调用数字(eg:four)的时候也能返回Fixnum对象。解决思路如下:
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(*args)
if args.size == 2
@operation = args[0]
@number = args[1]
end
end
%w(zero one two three four five six seven eight nine).each_with_index do { |w,i|
define_method(w) do
if @operation
@number.send(@operation, i)
else
i
end
end
}
end
第三种思路分析,通过代码块来延迟执行计算。代码如下:
class Calc
%w(zero one two three four five six seven eight nine).each_with_index{ |w,i|
define_method(w) do
@blk ? @blk.call(i) : (@number = i;self)
end
}
{plus: '+', minus: '-', times: '*', divided_by: '/'}.each {|k,v|
define_method(k) do
@blk = lambda{ |n| @number.send(v, n) }
self
end
}
end
测试代码:
require 'test/unit'
def test_calc
assert_equal(Calc.new.one.plus.nine, 10)
assert_equal(Calc.new.eight.minus.two, 4)
assert_equal(Calc.new.seven.times.three, 21)
assert_equal(Calc.new.six.divided_by.four, 1)
assert_equal(Calc.new.five.plus.four, 9)
end
以上三种方法都离不开define_method,method_missing,代码块,可见其在ruby中的重要性。