Friday, May 18, 2012

What are blocks in ruby and how are they useful?

Blocks in ruby are an incredible thing, and they are unlike anything in any other programming language, at least that I've encountered. A block is delimited by curly braces of a do...end section.


For example, arrays in ruby have a method called each (although technically it's an Enumerator, but think of it as basically a method for now). You can say:


a = [1,2,3]
a.each {|i| print i}


and ruby will output something like 123. The part inside the curly braces is a block. You can also have a block like this:


a.each do |i|
  print i
end


and it's the same thing. A block is basically a group of statements.


Each is basically a function that can take a block. To make your own function that can take blocks, you use the yield statement. For example, say you have a function like this (copied from ruby docs page):


def three_times
  yield
  yield
  yield
end


This yield statement is different in ruby than it is in, say python. Any function that has a yield statement inside it must take a block when it is called. You pass it a block by simply writing the block directly after the function call. Every time the execution in the function reaches a yield statement, the block is evaluated. So, writing three_times {print 'hello '} would output hello hello hello.


You can also pass values to the yield statement, like this:


def one_ten
  for i in 1..10
  yield i
  end
end


To access the value passed to yield, you write a block with a variable name in vertical lines at the front, like this: one_ten {|i| print i} or:


one_ten do |i|
  print i
end


Both of those will print 12345678910 to the output.


Here, we're really talking about iterators. Iterators are functions that have the yield statement in them, e.g. they can return one or more times with different values depending on how their yield statements are organized. In ruby, it seems that iterators are linked intrinsically to blocks.


One more thing. In python, for example, yield statements are sort of like return statements, but in ruby, yield statements actually evaluate to the last statement processed in the block. (Notice that technically, yield is not a statement, it's a function itself.) So you can have something like this, for example: 



def counting_up
a = 0
while a <= 10
a = yield a
end
end


counting_up do |yielded|
print yielded
yielded + 1
end

That will output 012345678910, because a will be set to whatever was yielded (a) plus 1 each time. Careful - if the yielded + 1 had not been there, the function would have run infinitely.

Blocks are useful in ruby in a couple of ways. One is the Array each method. There are other iterators as well built into ruby in different places: the Array.collect method, which takes a block and composes an array with the statements in the block applied to each item of the original array, is one. So something like [1,2,3].collect{|i| i+1} would output [2,3,4]. Again, look at the doc page for more examples.

So that's what blocks are in ruby, and how they're useful.

No comments:

Post a Comment