Reputation: 47
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]
First: When we called sorted = true
right inside the until
loop, that fulfills the condition of the sorted and it should end the loop.
Second: Can someone explain why the process of next if
inside the iteration method won't continue to iterate the whole array,
when it should have ended after the first iteration, and then call on sorted = true
.
I was asked to explain, and I was wondering if anyone can provide a better explanation.
Thank you Ruby Masters!
Upvotes: 2
Views: 633
Reputation: 432
First, until
is a top-testing loop. The entire block must loop before the until
condition is tested again; it is not tested after every statement within the loop. You have to reach end
or next
before the until
will evaluate again.
Second, <statement> if <condition>
is a ruby shorthand for if <condition> <statement> end
. It allows you to write a simple conditional on one line without sacrificing readability. The next
will only execute at the last iteration of the arr.each_index
loop, going up the stack to the until
condition, by which time sorted
will have been set to false
.
To see how this works, try running the following modification:
#!/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])
The output of this program is:
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]
This is of course academic: the proper way to sort the array would be with Array#sort.
Note: the next
is only necessary at all because on the last iteration of the each_index
loop, i + 1
will be out of range for the array, which would cause the next line which accesses arr[i + 1]
to fail (it will evaluate to nil
, and you can't compare an Integer to nil). Alternate ways of doing this would be to modify the conditional around the swap testing the index, or change the outside of the loop enumerating the array to be a smaller range.
Modifying the conditional, eliminating the next
, which works because logical-and conditionals are evaluated left to right and the interpreter stops as soon as the first one is false:
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])
Changing the range of the loop, which is better in that the loop executes fewer times:
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
is akin to goto
in other languages and should be avoided if possible.
Upvotes: 3