【问题标题】:How could I build a basic 'English calculator' in Ruby?如何在 Ruby 中构建一个基本的“英语计算器”?
【发布时间】:2014-02-13 14:03:36
【问题描述】:

所以我目前正在优秀的CodeWars 网站上进行 Ruby 培训,遇到了一个比我的水平略高的问题,但如果我能看一下/an,感觉它真的可以帮助我的编码开发回答并在我的大脑中推动一下。一种缺失链接类型的交易。

问题是如何构建Calc类来达到如下效果:

class Calc
end

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

该类只需要处理单个数字输入 0-9 和 + - * / 运算符,格式将始终是如上所示的 3 个方法链(即数字运算符号)。

我很想阅读您的一些解决方案,如果您不介意,请阅读您在解决它时所经历的思考过程。谢谢!

【问题讨论】:

  • 虽然我认为总的来说这是一个很好的问题(也是一个有趣的问题),但不幸的是 Stackoverflow 并不是这样一个相当广泛的问题的地方。 SO实际上更多的是针对特定的问题和答案;可以简明扼要地回答的事情等等。我认为您可能在某种论坛上对此有更多的运气,也许。或者,您可以尝试一些想法,如果您遇到问题或以前未在此处回答过的特定问题,您绝对可以发布。
  • 这更像是一个讨论请求而不是一个可以回答的问题,正如@teeg 所说,这违背了 Stackoverflow 的目的
  • 我已经在 codewars 上解决了这个 kata,并且将整个 kata 作为一个关于 SO 的问题询问完全违背了尝试 kata 的目的,你应该尝试 kata,当你遇到问题时,发布什么您遇到的问题

标签: ruby oop method-chaining


【解决方案1】:

不是最好的解决方案,但符合要求:

class Calc
  ENGLISH_TO_OP = Hash[%i{zero one two three four five six seven eight nine}
    .each_with_index.to_a].merge(plus: :+, minus: :-, times: :*, divided_by: :/)

  def method_missing(m)
    if s = ENGLISH_TO_OP[m]
      (@cmd ||= []) << s
      return @cmd[0].send(*@cmd[1..2]) if @cmd.size == 3
      self
    else
      super
    end
  end
end

允许:

2.1.0 :015 > Calc.new.seven.times.two
 => 14 
2.1.0 :016 > Calc.new.nine.divided_by.three
 => 3 
2.1.0 :017 > Calc.new.five.minus.six
 => -1 
2.1.0 :018 > Calc.new.one.plus.two
 => 3 

我不想写很多代码,所以method_missing 比必须定义方法要好,即使它可能会出现问题,因此我倾向于避免它。哈希查找适用于指定的操作数和操作,each_with_index 可以填充值。在条件中将查找的值分配给 s 只是懒惰。最后,我可以在初始化程序中初始化 @cmd,但这样代码更少,并且可以防止 @cmd 稍后被另一种方法重置为 nil。

【讨论】:

    【解决方案2】:

    这个怎么样?

    class Calc
      attr_accessor :result, :last_operator
    
      def initialize(result=0)
       @result = result
      end
    
      OPERATORS = {
        :plus => :+, :minus => :-, :times => :*, :divided_by => :/
      }
    
      OPERANDS = [ :zero, :one, :two, :three, :four, :five, :six, :seven, :eight, :nine, :ten, :eleven, :twelve ] 
    
      def method_missing(m, *args, &block)
        if OPERATORS.include?(m.to_sym)
          @last_operator = m.to_sym
          return self 
        elsif OPERANDS.include?(m.to_sym)
          if @last_operator.nil?
            @result = OPERANDS.index(m.to_sym)
            return self
          end
          @result = eval("#{@result} #{OPERATORS[@last_operator]} #{OPERANDS.index(m.to_sym)}")
          return @last_operator.nil? ? self : @result
        end
    
        super
      end
    end
    
    
    [20] pry(main)> Calc.new.three.times.seven
    => 21
    [21] pry(main)> Calc.new.twelve.divided_by.two
    => 6
    

    【讨论】:

    • 这将打印值而无需调用外部方法。如果您希望嵌套更多调用,则必须为 fixnum 执行 method_missing,并且可能必须覆盖“times”(因为 fixnum 已经有时间).. 这个解决方案完全解决了您的问题 =)
    • 哈哈.. 我相信你最终会做出一些疯狂的简单的事情! (查看下面@GarySWeaver 的解决方案)
    • 他认为运算符都是二元运算符。
    • 是的..他使用了一种不同的(有趣的)方式。
    【解决方案3】:

    假设方法不会以无效的方式使用:

    class Calc
      def self.operand name, v
        define_method(name) do
          @operator ? @operand.send(@operator, v) : (@operand = v; self)
        end
      end
      def self.operator name, v
        define_method(name) do
          @operator = v; self
        end
      end
    
      operand :one, 1
      operand :two, 2
      ...
      operator :plus, :+
      operator :minus, :-
      ...
    end
    

    【讨论】:

      【解决方案4】:

      这里还有一个使用#method_missing 的技巧:

      class Calc
          attr_reader :val
          def initialize
            # operand hash
            @hsh_num = {one: 1,two: 2, three: 3,four: 4,five: 5,six: 6}
            # operator hash
            @hsh_op = {plus: '+', minus: '-',times: '*',divided_by: '/'}
          end
          def method_missing(symbol)
            if @hsh_num[symbol]
              @val = @meth.is_a?(Method) ? @meth.call(@hsh_num[symbol]) : @hsh_num[symbol]
            end
            if @hsh_op[symbol]
              @meth = @val.method(@hsh_op[symbol])
            end
            self
          end
      end
      
      # I just added one more method val, to get the last output.
      # a bit change I did.
      Calc.new.one.plus.two.plus.two.val # => 3
      Calc.new.five.minus.six.val # => -1
      

      我并没有完全实现它。但你可以从中得到一个想法,适合你的情况。

      【讨论】:

      • 是的,最后打一个val 很容易:-)
      • @Abdo self 是那里的主要思想,我在每次方法调用后返回它。 :) 那么最后一个val 就像按等号在计算器上签名。
      • 当你不能使用val时,我不认为self是主要思想(因为最终你不能返回预期的输出)
      • @Abdo 这实际上是我的救命稻草.. :) 我该如何改进它?
      • 哈哈 :-) 您必须将无限链接限制为三个(否则,将不得不猴子补丁 Fixnum)...检查我下面的代码 =)
      猜你喜欢
      • 1970-01-01
      • 2014-01-17
      • 1970-01-01
      • 2018-04-04
      • 2018-11-09
      • 2013-06-17
      • 1970-01-01
      相关资源
      最近更新 更多