Как разорвать внешний цикл в Ruby?
в Perl есть возможность разорвать внешний цикл следующим образом:
AAA: for my $stuff (@otherstuff) {
for my $foo (@bar) {
last AAA if (somethingbad());
}
}
(синтаксис может быть неправильным), который использует метку петли разбить внешний цикл из внутреннего цикла. Есть ли что-то подобное в Ruby?
8 ответов:
то, что вы хотите, - это нелокальный поток управления, который Ruby имеет несколько вариантов:
- продолжений,
- исключения, и
throw
/catch
продолжения
плюсы:
- продолжения являются стандартным механизмом для нелокального управления потоком. На самом деле, вы можете построить любой нелокальный поток управления (подпрограммы, процедуры, функции, методы, сопрограммы, государственные машины, генераторы, условия, исключения) поверх них: они в значительной степени являются более приятным близнецом
GOTO
.плюсы:
- продолжения не являются обязательной частью спецификации языка Ruby, что означает, что некоторые реализации (XRuby, JRuby, Ruby.NET, IronRuby) не реализуют их. Таким образом, вы не можете полагаться на их.
исключения
плюсы:
- есть статья, которая математически доказывает, что исключения могут быть более мощными, чем продолжения. IOW: они могут делать все, что могут делать продолжения, и многое другое, поэтому вы можете использовать их в качестве замены для продолжений.
- исключения доступны повсеместно.
плюсы:
- они называются "исключения", что делает людей думайте, что они "только для исключительных обстоятельств". Это означает три вещи: кто-то, читающий ваш код, может не понять его, реализация может быть не оптимизирована для него (и, да, исключения are godawful медленно почти в любой реализации 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 ненавидит их даже больше, чем использование исключений для контроля потока.
считают
throw
/catch
. Обычно внешний цикл в приведенном ниже коде будет выполняться пять раз, но с помощью throw вы можете изменить его на все, что вам нравится, нарушая его в процессе. Рассмотрим этот совершенно правильный код ruby:catch (:done) do 5.times { |i| 5.times { |j| puts "#{i} #{j}" throw :done if i + j > 5 } } end
нет, нет.
ваши возможности:
- поместите цикл в метод и используйте return, чтобы вырваться из внешнего цикла
- установите или верните флаг из внутреннего цикла, а затем проверьте этот флаг во внешнем цикле и вырвитесь из него, когда флаг установлен (что является довольно громоздким)
- используйте throw/catch, чтобы вырваться из цикла
while c1 while c2 do_break=true end next if do_break end
или "перерыв, если do_break" в зависимости от того, что вы хотите
возможно, это то, что вы хотите? (не проверено)
stuff.find do |otherstuff| foo.find do somethingbad() && AAA end end
метод find продолжает цикл до тех пор, пока блок не вернет ненулевое значение или не будет достигнут конец списка.
Я знаю, что буду сожалеть об этом утром, но просто используя цикл 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
The
if y==5 && x==3
- это только пример выражения, которое становится истинным.
обертывание внутреннего метода вокруг петель может сделать трюк Пример:
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 и вернуться из внутреннего метода после выполнения условия.
вы можете рассмотреть возможность добавления флага, который установлен внутри внутреннего цикла, для управления внешним циклом.
"далее" внешний цикл
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
'разорвать' внешний цикл
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