break, next and redo
Bean Cart's barista works through the queue of orders. A cancelled order should be skipped. If the milk runs out, the barista stops for the day. Loops need the same controls: skip this one, stop completely, and, rarely, try this one again. Ruby's words for those are next, break and redo.
The rule
nextskips to the next turn,breakleaves the loop,redorepeats this turn.
next ends the current pass at once and moves on to the next item:
orders = ["latte", "cancelled", "tea", "mocha"]
orders.each do |drink|
next if drink == "cancelled"
puts "Make a #{drink}"
end
# Make a latte, Make a tea, Make a mochabreak leaves the whole loop immediately. Nothing after it in the loop runs, for this item or any later one:
milk = 2
orders.each do |drink|
next if drink == "cancelled"
break if milk.zero?
puts "Make a #{drink}"
milk -= 1
end
# Make a latte, Make a tea (then milk is 0, so it stops before mocha)break can also carry a value out of the loop. That value becomes the result of the whole loop expression:
first_big = [2, 6, 12, 3].each { |n| break n if n > 5 }
# first_big is 6Without a break, each returns the original array, so it is worth knowing which one you get.
Both keywords work in while, until, loop, each, times and similar. They are usually written with a modifier (next if ..., break if ...) as a guard near the top of the loop body.
redo restarts the current pass without moving to the next item and without re-checking the loop condition. It is rarely needed and easy to misuse: unless something inside changes, redo repeats the same pass forever. If you do use it, pair it with a limit, like a retry counter.
The shortcut
For each item, ask three questions in order: Should I stop everything? break. Should I skip just this one? next. Otherwise, do the work. Putting the guards at the top of the body keeps the rest of it simple.
"latte"
Not cancelled, and milk is 2, not zero. Make it; milk drops to 1.
Drills
Answer fast — trust the rule. Drills two to four use these loops:
[1, 2, 3, 4].each { |n| break if n == 3; print n } # loop A
[1, 2, 3, 4].each { |n| next if n.even?; print n } # loop B
result = [3, 8, 10].each { |n| break n if n > 5 } # loop CWhat does next do inside an each block?
What does loop A print?
What does loop B print?
After loop C, what is result?
Why is redo risky?
Remember
nextskips to the next turn,breakleaves the loop (and can return a value),redorepeats this turn.- Ask in order: stop everything? skip this one? otherwise, work. Put the guards at the top.
- Trap:
redowithout a limit loops forever, andeachreturns the array unless youbreakwith a value.
