Как нарушить внешний цикл в Ruby?
В Perl существует возможность разбить внешний цикл следующим образом:
AAA: for my $stuff (@otherstuff) {
for my $foo (@bar) {
last AAA if (somethingbad());
}
}
(синтаксис может быть неправильным), который использует метку цикла, чтобы нарушить внешний цикл из внутреннего цикла. Есть ли что-то подобное в Ruby?
Ответы
Ответ 1
Что вы хотите - это нелокальный поток управления, который Ruby имеет несколько вариантов:
- Продолжение,
- Исключения и
-
throw
/catch
Продолжения
Плюсы:
- Продолжение - это стандартный механизм нелокального управления потоком. На самом деле, вы можете построить любой нелокальный поток управления (подпрограммы, процедуры, функции, методы, сопрограммы, государственные машины, генераторы, условия, исключения): они в значительной степени являются более близким двойником
GOTO
.
Минусы:
- Продолжения не являются обязательной частью спецификации языка Ruby, что означает, что некоторые реализации (XRuby, JRuby, Ruby.NET, IronRuby) не реализуют их. Таким образом, вы не можете полагаться на них.
Исключения
Плюсы:
- Существует бумага, которая математически доказывает, что Исключения могут быть более мощными, чем Continuations. IOW: они могут делать все, что могут сделать продолжения, и многое другое, поэтому вы можете использовать их в качестве замены для продолжения.
- Исключения доступны повсеместно.
Минусы:
- Они называются "исключениями", которые заставляют людей думать, что они "только для исключительных обстоятельств". Это означает три вещи: кто-то, читающий ваш код, может не понимать его, реализация может быть не оптимизирована для него (и, да, исключения являются довольно медленными почти в любой реализации Ruby), и, что хуже всего, вы будете болеть за всех этих людей постоянно, бездумно бормоча "исключение только для исключительных обстоятельств", как только они заглядывают в ваш код. (Конечно, они даже не попытаются понять, что вы делаете.)
throw
/catch
Это (примерно), как бы это выглядело:
catch :aaa do
stuff.each do |otherstuff|
foo.each do |bar|
throw :aaa if somethingbad
end
end
end
Плюсы:
- То же, что и исключения.
- В Ruby 1.9 использование исключений для потока управления фактически является частью спецификации языка! Циклы, счетчики, итераторы и т.д. Используют исключение
StopIteration
для завершения.
Минусы:
- Сообщество Ruby ненавидит их даже больше, чем использование исключений для потока управления.
Ответ 2
Рассмотрим throw
/catch
. Обычно внешний цикл в приведенном ниже коде будет выполняться пять раз, но с броском вы можете изменить его на все, что захотите, сломав его в процессе. Рассмотрим этот вполне корректный код ruby:
catch (:done) do
5.times { |i|
5.times { |j|
puts "#{i} #{j}"
throw :done if i + j > 5
}
}
end
Ответ 3
Нет, нет.
Ваши варианты:
- поместите цикл в метод и используйте return для выхода из внешнего цикла
- установить или вернуть флаг из внутреннего цикла, а затем проверить этот флаг во внешнем цикле и выйти из него, когда флаг установлен (что является громоздким)
- использовать throw/catch для выхода из цикла
Ответ 4
while c1
while c2
do_break=true
end
next if do_break
end
или "break if do_break" в зависимости от того, что вы хотите
Ответ 5
Возможно, это то, что вы хотите? (не проверено)
stuff.find do |otherstuff|
foo.find do
somethingbad() && AAA
end
end
Метод find сохраняет цикл до тех пор, пока блок не вернет ненулевое значение или ударит конец списка.
Ответ 6
Я знаю, что буду сожалеть об этом утром, но просто используя цикл while мог бы сделать трюк.
x=0
until x==10
x+=1
y=0
until y==10
y+=1
if y==5 && x==3
x,y=10,10
end
end
break if x==10
puts x
end
if y==5 && x==3
- это только пример выражения true.
Ответ 7
Обертка внутреннего метода вокруг петель может сделать трюк
Пример:
test = [1,2,3]
test.each do |num|
def internalHelper
for i in 0..3
for j in 0..3
puts "this should happen only 3 times"
if true
return
end
end
end
end
internalHelper
end
Здесь вы можете выполнить проверку внутри любой из циклов for и вернуться из внутреннего метода после выполнения условия.
Ответ 8
Вы можете рассмотреть возможность добавления флага, который установлен во внутреннем цикле, для управления внешним циклом.
'next' внешний цикл
for i in (1 .. 5)
next_outer_loop = false
for j in (1 .. 5)
if j > i
next_outer_loop = true if j % 2 == 0
break
end
puts "i: #{i}, j: #{j}"
end
print "i: #{i} "
if next_outer_loop
puts "with 'next'"
next
end
puts "withOUT 'next'"
end
'break' внешний цикл
for i in (1 .. 5)
break_outer_loop = false
for j in (1 .. 5)
if j > i
break_outer_loop = true if i > 3
break
end
puts "i: #{i}, j: #{j}"
end
break if break_outer_loop
puts "i: #{i}"
end