有人可以解释为什么这个Ruby下一个工作在一个直到循环?

时间:2017-10-27 22:12:44

标签: ruby

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代码会起作用?

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

第二:有人可以解释为什么迭代方法中next if的过程不会继续迭代整个数组, 它应该在第一次迭代后结束,然后调用sorted = true

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

谢谢Ruby Masters!

1 个答案:

答案 0 :(得分:3)

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

其次,<statement> if <condition>if <condition> <statement> end的红宝石简写。它允许您在一行上写一个简单的条件而不会牺牲可读性。 next只会在arr.each_index循环的最后一次迭代中执行,从堆栈上升到until条件,此时sorted将设置为{false 1}}。

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

#!/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,如果可能,应该避免使用。