【问题标题】:Pulling indexes from an array and variable scope从数组和变量范围中提取索引
【发布时间】:2014-10-10 07:37:47
【问题描述】:

我正在运行 TestFirstRuby 并且一直被困在问题 12 上,即构建一个反向波兰表示法计算器。除了最后一个,我已经完成了所有测试,要求我取一个字符串(“1 2 3 * +”然后是“1 2 3 * + 4 5 / -”),然后评估表达式。

我要做的是将字符串转换为数组,将数字转换为整数,将运算符转换为符号,然后遍历数组并在涉及运算符的任何时候评估表达式。

这是代码的相关部分:

  def initialize
    @expression = ''
  end

  def tokens(string)
    tokens = string.split(' ')
    tokens.map! { |digit| /[0-9]/.match(digit) ? digit.to_i : digit.to_sym }
  end

  def evaluate(string)
    #1 2 3 * +
    #1 2 3 * + 4 5 - /
    total = 0
    @expression = tokens(string)
    @expression.map!{|item|
      index = @expression.index(item)
      if item == :+
        total = total + (@expression[index-1] + @expression[index-2])
        2.times {@expression.delete_at(index-1)}
      elsif item == :-
        total = total + (@expression[index-1] - @expression[index-2])
        2.times {@expression.delete_at(index-1)}
      elsif item == :x
        total = total + (@expression[index-1] * @expression[index-2])
        2.times {@expression.delete_at(index-1)}
      elsif item == :/
        total = total + (@expression[index-1].to_f / @expression[index-2])
        2.times {@expression.delete_at(index-1)}
      end
    }
    total
  end

我想要发生的是:对于数组中的每个项目,它会检查它是否与任何符号匹配。如果有匹配,它会将元素从符号向后两个空格更改为表达式的值(因此 2 3 * 变为 5 3 *)。然后,我试图删除运算符和它之前的整数,只留下评估值。我通过在运算符之前的索引上运行 delete_at 两次来做到这一点(理想情况下,5 3 * 转到 5 * 和然后只是 5)。然后它会移动到数组中的下一个元素。

我认为出了点问题,并且无法修复:我认为这里的变量范围出了点问题。我试图让表达式在每次在每个循环中当前所在的任何元素上运行代码时都被永久更改。对于每个元素,使用@expression.index(item) 设置一个变量'index'。这应该为每个循环中的每个元素重置。我认为发生的事情是每个循环的每次迭代都调用原始的@expression 数组,与每个循环的每次迭代相比都没有改变。

我得到的错误是说当它到达第一个测试字符串('1 2 3 * +')末尾的'+'时,它正在尝试使用:x添加,这意味着当它要求将两个变量加在一起(@expression[index-1] + @expression[index-2]),它正在拉动符号,我认为它应该已经从 @expression 中删除了。所以我希望评估为 6 + 1 的结果被评估为 3 + :x,这是行不通的。它是从原始数组中提取元素,而不是在数组发生变化时从数组中提取元素。

希望我解释得足够清楚。任何建议都会很棒。我在想有一些事情正在发生,但我找不到任何特定于这类问题的东西来帮助我。我尝试了不同的编码方式(.map、.each_with_index、.map.with_index 等),每次都遇到同样的问题。

【问题讨论】:

  • 请注意,您的正则表达式 /[0-9]/ 只允许一位非负整数。
  • 你的算法对5 1 2 + 4 × + 3 −做了什么?
  • @JörgWMittag,您的评论让我意识到我的回答存在根本问题,现在我已经更正了。非常感谢。

标签: ruby arrays scope


【解决方案1】:

您有大量的冗余代码。特别是,您复制了四个运算符中的每一个的操作。这是实现计算器的更类似于 Ruby 的方式。

代码

def evaluate(string)
  arr = create_arr(string)
  until arr.size == 1
    i = arr.index(arr.find { |e| e.is_a? Symbol })
    arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
    arr.delete_at(i)
    arr.delete_at(i-1)
  end
  arr.first
end

def create_arr(string)
  string.split(/\s+/).map { |e| e =~ /-?[0-9]+/ ? e.to_i : e.to_sym }
end

create_arr 中的行也可以以 : e } 结尾(sent 接受该方法的字符串或符号),在这种情况下,e.is_a? Symbol 将更改为 e.is_a? String

示例

evaluate("3 4 * 2 / 3 - 2 *")     #=>  6
evaluate("10    2 / 3 + 2 / 2 -") #=>  2
evaluate("8 -2 / 1 +")            #=> -3
evaluate("5 1 2 + 4 * + 3 -")     #=> 14

说明

假设

string = "2 3 4 * 2 / +"

第一步

arr = create_arr(string)              #=> [2, 3, 4, :*, 2, :/, :+]
arr.size == 1                         #=> false
  v = arr.find { |e| e.is_a? Symbol } #=> :*
  i = arr.index(v)                    #=> 3
  arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
    # arr[1] = arr[1].send(arr[3], arr[2])
    # arr[1] = 3.send(:*, 4)          #=> 12
  arr                                 #=> [2, 12, 4, :*, 2, :/, :+]
  arr.delete_at(i)                    #=> :*
  arr                                 #=> [2, 12, 4, 2, :/, :+]
  arr.delete_at(i-1)                  #=> 4
  arr                                 #=> [2, 12, 2, :/, :+]

第 2 步

arr.size == 1                         #=> false
  v = arr.find { |e| e.is_a? Symbol } #=> :/
  i = arr.index(v)                    #=> 3
  arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
    # arr[1] = arr[1].send(arr[3], arr[2])
    # arr[1] = 12.send(:/, 2)         #=> 6
  arr                                 #=> [2, 6, 2, :/, :+]
  arr.delete_at(i)                    #=> :/
  arr                                 #=> [2, 6, 2, :+]
  arr.delete_at(i-1)                  #=> 2
  arr                                 #=> [2, 6, :+]

第三步

arr.size == 1                         #=> false
  v = arr.find { |e| e.is_a? Symbol } #=> :+
  i = arr.index(v)                    #=> 2
  arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
    # arr[0] = arr[0].send(arr[2], arr[1])
    # arr[0] = 2.send(:+, 6)          #=> 8
  arr                                 #=> [8, 6, :+]
  arr.delete_at(i)                    #=> :+
  arr                                 #=> [8, 6]
  arr.delete_at(i-1)                  #=> 6
  arr                                 #=> [8]

第四步

arr.size == 1                         #=> true
arr.first                             #=> 8

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-08
    • 2017-07-03
    • 1970-01-01
    • 2019-02-04
    • 2013-11-25
    • 1970-01-01
    • 2020-04-01
    • 1970-01-01
    相关资源
    最近更新 更多