Когда и зачем использовать Loop в Ruby
недавно я столкнулся с проблемой/решением, которое использовало цикл. Я редко видел это до сих пор в моем обучении программированию Ruby (я новичок без опыта CS).
# 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
решение
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)
может кто-нибудь объяснить мне, когда лучше всего использовать конструкцию "цикл" вместо обычного "в то время как" или "если" или "каждая" конструкция?
4 ответов
без loop
, вы можете использовать while
построить так:
while( true ) {
# Do stuff until you detect it is done
if (done) break;
}
дело в том, что вы начинаете цикл, не зная, сколько итераций выполнять (или это трудно рассчитать заранее), но легко определить, когда цикл должен закончиться. Кроме того, для конкретного случая вы можете найти эквивалент while (! done) { # do stuff }
синтаксис неуклюжий, потому что условие done может произойти на полпути через цикл или в нескольких местах.
Руби loop
в основном то же самое как while( true )
- в самом деле вы можете использовать while( true )
почти наравне с ним.
в данном примере в каждой итерации есть следующие точки возврата:
if (left >= 0) && (arr[left] > arr[idx])
return left # <-- HERE
elsif (right < arr.length) && (arr[right] > arr[idx])
return right # <-- HERE
elsif (left < 0) && (right >= arr.length)
return nil # <-- HERE
end
здесь также подразумевается "else continue looping", если не выполнены конечные условия.
эти несколько возможных точек выхода, по-видимому, поэтому автор выбрал loop
построить, хотя есть много способов решения этой проблемы в потренируйся с Руби. Данный код решения не обязательно превосходит все остальные возможности.
добавление к предыдущим ответам,
конструкция "loop do" также предлагает более чистый синтаксис при работе с внешними итераторами, e.g
нет "loop do"
my_iterator = (1..9).each
begin
while(true)
puts my_iterator.next
end
rescue StopIteration => e
puts e
end
и теперь с "loop do" это станет
my_iterator = (1..9).each
loop do
puts my_iterator.next
end
и исключение обрабатывается для вас. Он также позволяет выполнять цикл через две коллекции одновременно, и как только в одной из них заканчиваются элементы, цикл выходит изящно,
iterator = (1..9).each
iterator_two = (1..5).each
loop do
puts iterator.next
puts iterator_two.next
end
будет печати: 1,1,2,2,3,3,4,4,5,5,6.
подробнее об этом на: ruby-docs.org
С помощью loop do
construct позволяет разбить на условное.
например:
i=0
loop do
i+=1
print "#{i} "
break if i==10
end
вы хотели бы использовать это, когда вы знаете количество элементов, которые будут обрабатываться, похож на for each
цикл
loop с конструкцией "loop" будет бесконечно выполнять данный блок пока код внутри блока не сломается при определенных условиях.
его можно использовать, когда у вас нет коллекции для цикла, места, где "каждый" и " для " не могут работать.
разница между "петлей" и while / until заключается в том, что while / until будет выполнить данный блок при выполнении определенного условия, где как в случай петли никакое условие, котор нужно начать, условие лежит внутри блок лупа.
для лучшего понимания прочитайте doc.
http://www.ruby-doc.org/core-1.9.2/Kernel.html#method-i-loop