【问题标题】:Changing the order of my code result in error ?更改我的代码顺序会导致错误?
【发布时间】:2017-08-09 18:49:09
【问题描述】:
     def stock_picker prices
        min_day , max_day , profit = 0 , 0 , 0
        i = 1
        while i < prices.length    
            (0...i).each do |day|
              if prices[i] - prices[day] > profit
                  min_day , max_day , profit = day , i , prices[i] - prices[day]
              end
                #i += 1
            end
            i += 1
        end

            return "[#{min_day}, #{max_day}]"
      end

        prices = [17,3,6,9,15,8,6,1,10]

        puts stock_picker prices

我的目标是实现一个方法#stock_picker,它接收一组股票价格,每个假设日都有一个。它应该返回一对代表最佳买入日和最佳卖出日的日期。天数从 0 开始。

我的问题是,如果我删除第 11 行并将其写在第 9 行,为什么这段代码不起作用。这将导致如下错误:

**PS C:\Users\dlim\mystuff> ruby stockpicker.rb
stockpicker.rb:8:in `block in stock_picker': undefined method `-' for nil:NilClass (NoMethodError)
        from stockpicker.rb:7:in `each'
        from stockpicker.rb:7:in `stock_picker'
        from stockpicker.rb:29:in `<main>'

【问题讨论】:

    标签: ruby


    【解决方案1】:

    您基本上是在尝试重写 combinationmax_by

    prices = [17, 3, 6, 9, 15, 8, 6, 1, 10]
    days = (0...prices.size).to_a
    p days.combination(2).max_by { |day1, day2| prices[day2] - prices[day1] }
    # => [1,4]
    

    如果您想要日期和相应的价格:

    [17,3,6,9,15,8,6,1,10].each.with_index.to_a.
                           combination(2).max_by{|(buy, day1), (sell, day2)|
                             sell-buy
                           }
    # => [[3, 1], [15, 4]]
    

    【讨论】:

      【解决方案2】:

      这是在哪里发生的?

      你的错误

      stockpicker.rb:8:in `block in stock_picker': undefined method `-' for nil:NilClass (NoMethodError)
         from stockpicker.rb:7:in `each'
         from stockpicker.rb:7:in `stock_picker'
         from stockpicker.rb:29:in `<main>'
      

      发生在第 8 行

      if prices[i] - prices[day] > profit
      

      当它尝试访问prices[i]i = 9 并且prices 返回nil, 它不响应减号- 运算符。

      为什么会这样?

      你是循环中的循环

      (0...i).each do |day|
        if prices[i] - prices[day] > profit
            min_day , max_day , profit = day , i , prices[i] - prices[day]
        end
        #i += 1
      end
      

      在这里增加i 索引计数器变量并没有什么意义,因为 day 已经在迭代范围 (0...i) 和 在此循环内增加i 意味着它比较prices 中的每个值 数组 onceprices 内部数组中的下一个 day 值,其中 将仅包括 prices 的前三个值(意味着在 prices 数组的末尾,如 110 永远不会被比较 另一个);例如

      i = 1
      prices = [17,3,6,9,15,8,6,1,10]
      
      # iteration 0
      if prices[i] - prices[day] > profit
      #          3 - 17 > 0 # => false
      
      
      # iteration 1
      i += 1 # => 2
      day # => 0
      
      if prices[i] - prices[day] > profit
      #          6 - 17 > 0 # => false
      
      i += 1 # => 3
      day # => 1
      
      
      # iteration 2
      if prices[i] - prices[day] > profit
      #          9 - 3 > 0 # => true
      min_day, max_day, profit = 1, 3, 6
      
      i += 1 # => 4
      day # => 0
      
      
      # iteration 3
      if prices[i] - prices[day] > profit
      #          15 - 17 > 0 # => false
      
      i += 1 # => 5
      day # => 1
      
      
      # iteration 4
      if prices[i] - prices[day] > profit
      #          8 - 3 > 0 # => true
      
      min_day, max_day, profit = 1, 5, 5
      i += 1 # => 6
      day # => 2
      
      
      # iteration 5
      if prices[i] - prices[day] > profit
      #          6 - 6 > 0 # => false
      
      i += 1 # => 7
      day # => 3
      
      
      # iteration 6
      if prices[i] - prices[day] > profit
      #          1 - 9 > 0 # => false
      
      i += 1 # => 8
      day # => 0
      
      
      # iteration 7
      if prices[i] - prices[day] > profit
      #         10 - 17 > 0 # => false
      
      i += 1 # => 9
      day # => 1
      
      
      # iteration 8
      if prices[i] - prices[day] > profit
      #        nil - 3 > 0 # => NoMethodError
      

      在第 8 次迭代中,外部循环在 使用prices[i] 访问价格数组,但仍在 在第 5 次迭代后设置的范围为 (0...7) 的第二个循环,所以 它没有到达您的 while 循环的转义子句/表达式 while i &lt; prices.length.

      可能的解决方案:

      您可以保留您的工作解决方案,或者您可以通过使用来简化您的代码 另一个 Range 作为外循环

      (1...prices.length).each do |i|
        # ...
      end
      

      而不是在 while 循环中增加索引计数器变量

      i = 1
      while i < prices.length
        # ...
        i +=1
      end
      

      看起来像这样

      def stock_picker prices
        min_day , max_day , profit = 0 , 0 , 0
        (1...prices.length).each do |i|
          (0...i).each do |day|
            if prices[i] - prices[day] > profit
              min_day , max_day , profit = day , i , prices[i] - prices[day]
            end
          end
        end
      
        return "[#{min_day}, #{max_day}]"
      end
      
      prices = [17,3,6,9,15,8,6,1,10]
      
      puts stock_picker prices
      

      它将根据您的要求在接下来的几天内迭代

      [i, day]
      # => [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0],
      #    [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 1], [8, 1],
      #    [3, 2], [4, 2], [5, 2], [6, 2], [7, 2], [8, 2],
      #    [4, 3], [5, 3], [6, 3], [7, 3], [8, 3],
      #    [5, 4], [6, 4], [7, 4], [8, 4],
      #    [6, 5], [7, 5], [8, 5],
      #    [7, 6], [8, 6],
      #    [8, 7]
      

      更新:

      您还可以使用Ruby Array combination method 再次简化它

      (0...prices.length).to_a.combination(2)
      

      生成与迭代隐含的 Ranges 相同的唯一且不重复的天对,如下所示

      def stock_picker prices
        min_day , max_day , profit = 0 , 0 , 0
        (0...prices.length).to_a.combination(2).each do |day, i|
          if prices[i] - prices[day] > profit
            min_day , max_day , profit = day , i , prices[i] - prices[day]
          end
        end
      
        return "[#{min_day}, #{max_day}]"
      end
      
      prices = [17,3,6,9,15,8,6,1,10]
      
      puts stock_picker prices
      

      |day, i| 将访问组合数组中日期索引对数组中的第一个和第二个变量,同时重用您已使用的现有变量名称。

      【讨论】:

      • 您好,在迭代示例中,您是如何计算天数的?每次根据范围(0...1)迭代时,天数不应该只是+1。据我了解,在迭代 0 时,day 应该是 0,在迭代 1 时,day 应该是 1,迭代 2 day 应该是 2,依此类推。我想我误解了那里的某些部分。
      • 重新运行您的原始代码我得到以下一对[i, day] 在它到达nil 之前:[1, 0], [2, 0], [3, 1], [4, 0], [5, 1], [6, 2], [7, 3], [8, 0], [9, 1]。迭代 0 和迭代 1 两次是 0 的原因是因为使用了包含与排除范围;例如(0..1).to_a # =&gt; [0, 1](0...1).to_a # =&gt; [0]。因此 Range (0...1) 将运行一次,即迭代 0,而 Range (0...2) 将运行两次,即迭代 1 和 2。 > 使用 .. 构造的范围从头到尾运行。使用... 创建的不包括最终值。
      猜你喜欢
      • 2020-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-28
      • 1970-01-01
      相关资源
      最近更新 更多