Saturday, July 26, 2008

Ruby's block always make a developer like me who is being with typed language for long time confused.

In Ruby, there are some methods called Iterators like times, each, map, upto and downto and some objects called Enumerable objects like array, hash and range which contain many Iterator methods. The common feature of these methods is that they are all interacted with some custom codes.

A group of these codes for a method is called "a block".

For example:

[1,2,3].map {|x| x*x}

[1,2,3] is an Enumerable object(an Array)

"map" is an Iterator(method) which will transverse all elements in the Array

{|x| x*x} is a block provide by user for callback from "map" method

a "yield" method is provided in "map" to call block code

So, block is a group of custom codes waiting for Iterator method to call. Each time when a "yield" is met, block is called.

Here is a very simple example about an Iterator method call "twice".

def twiceyieldyieldend

When you call this iterator, it just yield twice that means the caller's block will be invoked twice.

twice {puts "hello"}

output: hello hello.

Of course you can pass value from Iterator method to yield block.

def sequence(n, m, c) i = 0 while(i < n) # Loop n times #Invoke the block and pass a value to it yield m*i + c i += 1 # Increment i each time endend

The Iterator method is invoked by: sequence(3, 5, 1) {|y| puts y }

The passing block parameter ({|y| puts y}) can be explicitly declared in the Iterator. Once it is declared, "call" method is used to invoke the block instead of "yield".

def sequence(n, m, c, &b) i = 0 while(i < n) # Loop n times #Invoke the block and pass a value to it b.call(m*i + c) i += 1 # Increment i each time endend

The Iterator method is invoked by: sequence(3, 5, 1) {|y| puts y } # Note that the block is still passed outside of the parentheses