Когда и зачем использовать 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