【问题标题】:Can someone explain why this Ruby next if works in an until loop?有人可以解释为什么这个 Ruby 下一个 if 在 until 循环中工作吗?
【发布时间】:2017-10-27 22:12:43
【问题描述】:
def sorting(arr)
  sorted = false
  until sorted 
    sorted = true
    arr.each_index do |i|
      next if i == arr.length - 1 
      if arr[i] > arr[i + 1]
        arr[i], arr[i+1] = arr[i+1], arr[i]
        sorted = false
      end
    end
  end
  arr
end

p sorting([7, 4, 5, 2, 1, 3]) => [1, 2, 3, 4, 5, 7]

我的问题是:为什么上面的 Ruby 代码可以工作?

首先:当我们在until 循环内调用sorted = true 时,它满足了排序的条件,它应该结束循环。

第二:谁能解释一下为什么迭代方法里面next if的过程不会继续迭代整个数组, 当它应该在第一次迭代后结束时,然后调用sorted = true

我被要求解释,我想知道是否有人可以提供更好的解释。

感谢 Ruby 大师!

【问题讨论】:

    标签: ruby


    【解决方案1】:

    首先,until 是一个顶级测试循环。在再次测试until 条件之前,整个块必须循环;在循环中的每个语句之后都不会对其进行测试。您必须先到达endnextuntil 才会再次评估。

    其次,<statement> if <condition>if <condition> <statement> end 的 ruby​​ 简写。它允许您在不牺牲可读性的情况下在一行上编写一个简单的条件。 next 只会在arr.each_index 循环的最后一次迭代中执行,向上堆栈到until 条件,此时sorted 将被设置为false

    要查看其工作原理,请尝试运行以下修改:

    #!/usr/bin/ruby
    
    def sorting(arr)
      puts "starting with #{arr}"
      sorted = false
      until sorted 
        sorted = true
        arr.each_index do |i|
          if i == arr.length - 1 
            puts "'next' when i == #{i}, arr = #{arr}"
            next
          end
          if arr[i] > arr[i + 1]
            puts "swapping at #{i}: #{arr[i]} <=> #{arr[i+1]}"
            arr[i], arr[i+1] = arr[i+1], arr[i]
            sorted = false
          end
         end
       end
       arr
    end
    
    p sorting([7,4,5,1,2,3])
    

    这个程序的输出是:

    starting with [7, 4, 5, 1, 2, 3]
    swapping at 0: 7 <=> 4
    swapping at 1: 7 <=> 5
    swapping at 2: 7 <=> 1
    swapping at 3: 7 <=> 2
    swapping at 4: 7 <=> 3
    'next' when i == 5, arr = [4, 5, 1, 2, 3, 7]
    swapping at 1: 5 <=> 1
    swapping at 2: 5 <=> 2
    swapping at 3: 5 <=> 3
    'next' when i == 5, arr = [4, 1, 2, 3, 5, 7]
    swapping at 0: 4 <=> 1
    swapping at 1: 4 <=> 2
    swapping at 2: 4 <=> 3
    'next' when i == 5, arr = [1, 2, 3, 4, 5, 7]
    'next' when i == 5, arr = [1, 2, 3, 4, 5, 7]
    [1, 2, 3, 4, 5, 7]
    

    这当然是学术性的:对数组进行排序的正确方法是使用Array#sort

    注意:next 只是必要的,因为在each_index 循环的最后一次迭代中,i + 1 将超出数组的范围,这将导致访问arr[i + 1] 的下一行失败(它将评估为nil,并且您无法将整数与 nil 进行比较)。这样做的替代方法是修改交换测试索引周围的条件,或者将枚举数组的循环外部更改为更小的范围。

    修改条件,消除next,因为逻辑和条件是从左到右评估的,解释器在第一个为假时立即停止:

    def sorting(arr)
      sorted = false
      until sorted 
        sorted = true
        arr.each_index do |i|
          if (i < arr.size - 1) && (arr[i] > arr[i + 1])
            arr[i], arr[i+1] = arr[i+1], arr[i]
            sorted = false
          end
         end
       end
       arr
    end
    
    p sorting([7,4,5,1,2,3])
    

    改变循环的范围,这样循环执行次数更少:

    def sorting(arr)
      sorted = false
      until sorted 
        sorted = true
        (0..arr.size - 2).each do |i|
          if (arr[i] > arr[i + 1])
            arr[i], arr[i+1] = arr[i+1], arr[i]
            sorted = false
          end
         end
       end
       arr
    end
    
    p sorting([7,4,5,1,2,3])
    

    next 类似于其他语言中的goto,应尽可能避免使用。

    【讨论】:

    • 哇!感谢您的详细解释。这很有意义。不过,我还有一个问题:如果我将这一行代码“sorted = true”带入直到循环中,在迭代结束后将其移至该行,循环在完全排序之前结束。有没有理由为什么这种方式行不通? def sorting(arr) sorted = false until sorted (0..arr.size - 2).each do |i| if (arr[i] &gt; arr[i + 1]) arr[i], arr[i+1] = arr[i+1], arr[i] sorted = false end end sorted = true end arr end p sorting([7,4,5,1,2,3])
    • sorted 变量背后的想法是作为一个标志来操作,控制until 循环是否会再次执行。如果在until 循环结束之前将sorted 设置为true,则可以保证until 循环只会执行一次。 sorted = false 仅发生 如果 数组发生某些事情,将有最后一次通过,只是为了看看没有什么可做的。您首先将其设置为 true,然后仅在需要时才设置为 false。
    • 酷,谢谢,汤姆。我只是自然而然地学会了这一点,但从未真正考虑过差异或原因。这次我吸取了很好的教训!谢谢1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-11
    • 2015-10-16
    • 1970-01-01
    • 2019-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多