Stopping an Iteration

Problem

You want to interrupt an iteration from within the code block you passed into it.

Solution

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

 

Discussion

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.

See Also

  • Recipe 6.12, "Walking a Directory Tree"
  • ri Find


Strings

Numbers

Date and Time

Arrays

Hashes

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

Internet Services

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

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net