Becoming a Blockhead
About a week ago, I tried my best to explain the ins and outs of blocks in Ruby. The basic gist was that blocks were snazzy syntax for anonymous functions.
So what makes blocks so cool? This time I want to take a look at a few of the uses blocks are put to in Ruby.
What’s more, we’re going to define all the functions we use that take blocks ourselves. Most of the functions we’ll define exist already in the standard Ruby implementation, but hopefully by seeing them defined first-hand, you’ll get a better understanding of why blocks are so useful.
Before we begin, here’s something snazzy about Ruby you might not have known.
We’ll be making use of it in all of the examples.
You can take a preexisting class, like Array,
and add methods to it whenever you want.
You can even override existing methods.
class Array def say_hello "Hello from #{self.inspect}!" end end [1, 2, 3].say_hello #=> "Hello from [1, 2, 3]!"
Within a preexisting class,
the self variable still refers to the current instance of that class,
just like in objects you make from scratch.
Now, onto the blocks!
Counting
If you’ll recall, last time
we tried to mimic Javascript’s for loop.
It was pretty awkward.
Wouldn’t it be nice if we had some method to count for us?
Well, we can make one, using the power of blocks.
Let’s call it times;
it sounds pleasantly English-like to say “5 times do whatever.”
So how do we want it to work?
Probably a lot like our previous attempt at counting.
Get an integer and keep increasing it until it hits the number we’re counting to.
This’ll be self in the code.
Let’s give it a go:
class Integer def times i = 0 while i < self yield # Remember: this means "call the block" i += 1 end end end
Sweet! Let’s give it a try:
# Print "Hello!" five times 5.times { puts "Hello!" } # Hello! # Hello! # Hello! # Hello! # Hello!
There is one thing about this that’s a little sad. Sure, we’re calling the block five times, which is what we wanted to do. But we’re not being as useful as we could.
What if, for instance, we wanted to add up all the numbers less than 100? We’d have to keep a counter to keep track of which number we were on. But there’s already a counter in the method! Can’t we just use that?
We sure can; all we have to do is yield it:
class Integer def times i = 0 while i < self yield i # This means "call the block and pass it i" i += 1 end end end
Now it’s easy to add up the numbers:
sum = 0 100.times { |i| sum += i } sum #=> 4950
But wait! Won’t that break our old use, where our block didn’t take an argument? Actually, it won’t: you’re allowed to yield more values than a block takes. Ruby won’t complain; everything will work.
This leads to a rule of thumb: when in doubt, pass more information to the block. It doesn’t have to use it.
Dealing With Arrays
The thing about arrays is that you never know how many elements one of them has. It could be one; it could be one million. And most of the time, we want to be able to do stuff to all of them.
Thus, programming languages provide a way to loop through the arrays.
Ruby’s way is to use blocks.
The typical format is a method called each
that calls its block on each element in the array in turn.
We can implement it using times:
class Array def each self.length.times { |i| yield self[i] } end end ["Hello", "blocky", "world!"].each { |e| puts e } # Hello # blocky # world!
All we’re doing here is yielding each successive element of the array.
each is actually such a useful method,
there’s a special syntax that you can use for it.
It’s Ruby’s own sort of for loop:
for e in [1, 2, 3] e += 5 puts e end # 6 # 7 # 8
This is exactly the same as saying
do |e| e += 5 puts e end
Now we can use each to implement map from last time:
class Array def map new_array = [] self.each { |e| new_array << yield(e) } return new_array end end [1, 2, 3].map { |e| 4 - e } #=> [3, 2, 1]
There’s a lot more we could do with arrays, but for now let’s move on.
Resource Management
Due to some quirks of how file systems work, whenever you open a file in any langauge, you have to remember to close it when you’re done. Otherwise, bad things happen.
In most object-oriented languages,
the way to do this is to call a close method
on the file when we’re done with it.
This works in Ruby, too:
file = File.new("/home/nex3/etc/blog") lines = [] until file.eof? lines << file.gets end file.close
But that’s kind of annoying. You tend to sometimes forget to close the file, especially if there’s a lot of code in between opening and closing.
Blocks can come to the rescue here, as well. If we wrap the code using the file in a block, we can get it to automatically close when we’re done.
class File def self.open(filename) file = File.new(filename) yield file file.close end end
Now we can write the previous bit of code like so:
File.open("/home/nex3/etc/blog") do |file| lines = [] until file.eof? lines << file.gets end end
Alright, this is all I have time for now. Look forward to yet another entry on blocks - this time on some crazy blocky magic - in the near future.
About Me
Feed
I've Been Podcasted!



I think there is typo mistake:
should be
Oh, good point. Fixed.
another : def open(filename)
open should be a class method: def self.open(filename)
Even if it is a little out of the context of the post, I’d rather write the File.open like this:
Like this, if an exception is raised in the block. the file will still be closed.