Code Blocks and Iteration

In Ruby, a code block (or just "block") is an object that contains some Ruby code, and the context neccesary to execute it. Code blocks are the most visually distinctive aspect of Ruby, and also one of the most confusing to newcomers from other languages. Essentially, a Ruby code block is a method that has no name.

Most other languages have something like a Ruby code block: C's function pointers, C++'s function objects, Python's lambdas and list comprehensions, Perl's anonymous functions, Java's anonymous inner classes. These features live mostly in the corners of those languages, shunned by novice programmers. Ruby can't be written without code blocks. Of the major languages, only Lisp is more block-oriented.

Unlike most other languages, Ruby makes code blocks easy to create and imposes few restrictions on them. In every other chapter of this book, you'll see blocks passed into methods like it's no big deal (which it isn't):

	[1,2,3].each { |i| puts i}
	# 1
	# 2
	# 3

In this chapter, we'll show you how to write that kind of method, the kinds of method that are useful to write that way, and when and how to treat blocks as first class objects.

Ruby provides two syntaxes for creating code blocks. When the entire block will fit on one line, it's most readable when enclosed in curly braces:

	[1,2,3].each { |i| puts i }
	# 1
	# 2
	# 3

When the block is longer than one line, it's more readable to begin it with the do keyword and end it with the end keyword:

do |i|
	 if i % 2 == 0
	 puts "#{i} is even."
	 puts "#{i} is odd."
	# 1 is odd.
	# 2 is even.
	# 3 is odd.

Some people use the bracket syntax when they're interested in the return value of the block, and the do…end syntax when they're interested in the block's side effects.

Keep in mind that the bracket syntax has a higher precedence than the do..end syntax. Consider the following two snippets of code:

	1.upto 3 do |x|
	 puts x
	# 1
	# 2
	# 3

	1.upto 3 { |x| puts x }
	# SyntaxError: compile error

In the second example, the code block binds to the number 3, not to the function call 1.upto 3. A standalone variable can't take a code block, so you get a compile error. When in doubt, use parentheses.

	1.upto(3) { |x| puts x }
	# 1
	# 2
	# 3

Usually the code blocks passed into methods are anonymous objects, created on the spot. But you can instantiate a code block as a Proc object by calling lambda. See Recipe 7.1 for more details.

	hello = lambda { "Hello" }
	# => "Hello"

	log = lambda { |str| puts "[LOG] #{str}" }"A test log message.")
	# [LOG] A test log message.

Like any method, a block can accept arguments. A block's arguments are defined in a comma-separated list at the beginning of the block, enclosed in pipe characters:

	{1=>2, 2=>4}.each { |k,v| puts "Key #{k}, value #{v}" }
	# Key 1, value 2
	# Key 2, value 4

Arguments to blocks look almost like arguments to methods, but there are a few restrictions: you can't set default values for block arguments, you can't expand hashes or arrays inline, and a block cannot itself take a block argument.[1]

[1] In Ruby 1.9, a block can itself take a block argument: |arg1, arg2, &block|. This makes methods like Module#define_method more useful. In Ruby 2.0, you'll be able to give default values to block arguments.

Since Proc objects are created like other objects, you can create factory methods whose return values are customized pieces of executable Ruby code. Here's a simple factory method for code blocks that do multiplication:

	def times_n(n)
	 lambda { |x| x * n }

The following code uses the factory to create and use two customized methods:

	times_ten = times_n(10) # => 50 # => 12.5

	circumference = times_n(2*Math::PI) # => 62.8318530717959 # => 18.8495559215388
	[1, 2, 3].collect(&circumference)
	# => [6.28318530717959, 12.5663706143592, 18.8495559215388]

You may have heard people talking about Ruby's " closures." What is a closure, and how is it different from a block? In Ruby, there is no difference between closures and blocks. Every Ruby block is also a closure.[2]

[2] Someone could argue that a block isn't really a closure if it never actually uses any of the context it carries around: you could have done the same job with a "dumb" block, assuming Ruby supported those. For simplicity's sake, we do not argue this.

So what makes a Ruby block a closure? Basically, a Ruby block carries around the context in which it was defined. A block can reference the variables that were in scope when it was defined, even if those variables later go out of scope. Here's a simple example; see Recipe 7.4 for more.

ceiling = 50
	# Which of these numbers are less than the target?
	[1, 10, 49, 50.1, 200].select { |x| x < 
ceiling }
	# => [1, 10, 49]

The variable ceiling is within scope when the block is defined, but it goes out of scope when the flow of execution enters the select method. Nonetheless, the block can access ceiling from within select, because it carries its context around with it. That's what makes it a closure.

We suspect that a lot of people who say "closures" when talking about Ruby blocks just do it to sound smart. Since we've already ruined any chance we might have had at sounding smart, we've decided refer to Ruby closures as just plain "blocks" throughout this book. The only exceptions are in the rare places where we must discuss the context that makes Ruby's code blocks real closures, rather than "dumb" blocks.



Date and Time



Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming


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 © 2008-2020.
If you may any questions please contact us: