I recently came up across a problem/solution that used Loop Do. I seldom have seen this so far in my learning Ruby Programming (I am a beginner with no CS experience).
# Write a function, `nearest_larger(arr, i)` which takes an array and an
# index. The function should return another index, `j`: this should
# satisfy:
#
# (a) `arr[i] < arr[j]`, AND
# (b) there is no `j2` closer to `i` than `j` where `arr[i] < arr[j]`.
#
# In case of ties (see example beow), choose the earliest (left-most)
# of the two indices. If no number in `arr` is largr than `arr[i]`,
# return `nil`.
#
# Difficulty: 2/5
describe "#nearest_larger" do
it "handles a simple case to the right" do
nearest_larger([2,3,4,8], 2).should == 3
end
it "handles a simple case to the left" do
nearest_larger([2,8,4,3], 2).should == 1
end
it "treats any two larger numbers like a tie" do
nearest_larger([2,6,4,8], 2).should == 1
end
it "should choose the left case in a tie" do
nearest_larger([2,6,4,6], 2).should == 1
end
it "handles a case with an answer > 1 distance to the left" do
nearest_larger([8,2,4,3], 2).should == 0
end
it "handles a case with an answer > 1 distance to the right" do
nearest_larger([2,4,3,8], 1).should == 3
end
it "should return nil if no larger number is found" do
nearest_larger( [2, 6, 4, 8], 3).should == nil
end
end
SOLUTION
def nearest_larger(arr, idx)
diff = 1
loop do
left = idx - diff
right = idx + diff
if (left >= 0) && (arr[left] > arr[idx])
return left
elsif (right < arr.length) && (arr[right] > arr[idx])
return right
elsif (left < 0) && (right >= arr.length)
return nil
end
diff += 1
end
end
nearest_larger([2,4,3,8], 1)
Can someone please explain to me when is the best time to use a "loop do" construct instead of the usual "while" or "unless" or "each" construct?
Adding up to the previous answers,
The "loop do" construct also offers a cleaner syntax when working with external iterators, e.g
No "loop do"
my_iterator = (1..9).each
begin
while(true)
puts my_iterator.next
end
rescue StopIteration => e
puts e
end
And now with "loop do" this would become
my_iterator = (1..9).each
loop do
puts my_iterator.next
end
And the exception is handled for you. It also allows you to loop through two collections at the same time and as soon as one of them runs out of elements the loop exits gracefully,
iterator = (1..9).each
iterator_two = (1..5).each
loop do
puts iterator.next
puts iterator_two.next
end
It will print: 1,1,2,2,3,3,4,4,5,5,6.
More info on it at: ruby-docs.org