You want to interrupt an iteration from within the code block you passed into it.
The simplest way to interrupt execution is to use break. A break statement will jump out of the closest enclosing loop defined in the current method:
1.upto(10) do |x| puts x break if x == 3 end # 1 # 2 # 3
The break statement is simple but it has several limitations. You can't use break within a code block defined with Proc.new or (in Ruby 1.9 and up) Kernel#proc. If this is a problem for you, use lambda instead:
aBlock = Proc.new do |x| puts x break if x == 3 puts x + 2 end aBlock.call(5) # 5 # 7 aBlock.call(3) # 3 # LocalJumpError: break from proc-closure
More seriously, you can't use break to jump out of multiple loops at once. Once a loop has run, there's no way to know whether it completed normally or by using break.
The simplest way around this problem is to enclose the code you want to skip within a catch block with a descriptive symbolic name. You can then throw the corresponding symbol when you want to jump to the end of the catch block. This lets you skip out of any number of nested loops and method calls.
The tHRow/catch syntax isn't exception handlingexceptions use a raise/rescue syntax. This is a special flow control construct designed to replace the use of exceptions for flow control (as sometimes happens in Java programs). It's a bit like an old style global GOTO, capable of suddenly moving execution to a faraway part of your program. It keeps your code more readable than a GOTO, though, because it's restricted: a tHRow can only jump to the end of a corresponding catch block.
The best example of the catch..throw syntax is the Find.find function described in Recipe 6.12. When you pass a code block into Find.find, it yields up every directory and file in a certain directory tree. When your code block is given a directory, it can stop find from recursing into that directory by calling Find.prune, which throws a :prune symbol. Using break would stop the find operation altogether; throwing a symbol lets Find.prune know to just skip one directory.
Here's a simplified view of the Find.find and Find.prune code:
def find(*paths) paths.each do |p| catch(:prune) do # Process p as a file or directory… end # When you call Find.prune you'll end up here. end end def prune throw :prune end
When you call Find.prune, execution jumps to immediately after the catch(:prune) block. Find.find then starts processing the next file or directory.
Date and Time
Files and Directories
Code Blocks and Iteration
Objects and Classes8
Modules and Namespaces
Reflection and Metaprogramming
XML and HTML
Graphics and Other File Formats
Databases and Persistence
Web Development Ruby on Rails
Web Services and Distributed Programming
Testing, Debugging, Optimizing, and Documenting
Packaging and Distributing Software
Automating Tasks with Rake
Multitasking and Multithreading
Extending Ruby with Other Languages