Passing Blocks (Not Procs) into Methods

OK, so this has been more theoretically cool than actually cool, partly because this is all a bit of a hassle to use. I’m man enough to admit that. A lot of the problem is that we have to go through three steps (defining the method, making the proc, and calling the method with the proc) when it sort of feels like there should be only two (defining the method and passing the block of code right into the method, without using a proc at all), since most of the time you don’t want to use the proc/block after you pass it into the method.

It should be…more like how iterators work! Sho’ nuff, baby:

class​ Array
def​ each_even(&was_a_block__now_a_proc)
# We start with "true" because
# arrays start with 0, which is even.
is_even = true
self.each ​do​ |object|
if​ is_even
was_a_block__now_a_proc.call object
end
# Toggle from even to odd, or odd to even.
is_even = !is_even
end
end
end
fruits = [​'apple'​, ​'bad apple'​, ​'cherry'​, ​'durian'​]
fruits.each_even ​do​ |fruit|
puts ​"Yum! I just love ​#{fruit}​ pies, don't you?"
end
# Remember, we are getting the even-numbered *elements*
# of the array, which in this case are all odd numbers,
# because I live only to irritate you.
[1, 2, 3, 4, 5].each_even ​do​ |odd_ball|
puts ​"​#{odd_ball}​ is NOT an even number!"
end
Yum! I just love apple pies, don't you?
Yum! I just love cherry pies, don't you?
1 is NOT an even number!
3 is NOT an even number!
5 is NOT an even number!

To pass in a block to each_even, all we had to do was stick the block after the method. You can pass a block into any method this way, though many methods will just ignore the block. In order to make your method not ignore the block but grab it and turn it into a proc, put the name of the proc at the end of your method’s parameter list, preceded by an ampersand (&). So, that part is a little tricky but not too bad, and you have to do that only once (when you define the method). Then you can use the method over and over again, just like the built-in methods that take blocks, such as each and times. (Remember 5.times do…? What a cutie….)

If you get confused (I mean, there’s this each and its block inside each_even), just remember what each_even is supposed to do: call the block passed in with every other element in the array. Once you’ve written it and it works, you don’t need to think about what it’s actually doing under the hood (“which block is called when?”); in fact, that’s exactly why we write methods like this—so we never have to think about how they work again. We just use them.

I remember one time I wanted to profile some code I was writing; you know, I wanted to time how long it took to run. I wrote a method that takes the time before running the code block, then runs it, then takes the time again at the end, and finally figures out the difference. And it went a little something like this:

def​ profile block_description, &block
start_time = Time.new
block.call
duration = Time.new - start_time
puts ​"​#{block_description}​: ​#{duration}​ seconds"
end
profile ​'25000 doublings'​ ​do
number = 1
25000.times ​do
number = number + number
end
puts ​"​#{number.to_s.length}​ digits"
# That's the number of digits in this HUGE number.
end
profile ​'count to a million'​ ​do
number = 0
1000000.times ​do
number = number + 1
end
end
7526 digits
25000 doublings: 0.044003 seconds
count to a million: 0.072004 seconds

How simple! How elegant! Dude, admit it: you think I’m cool. With that tiny method, we can now easily time any section of any program; we just throw the code in a block, send it to profile, and do a little dance…. What could be simpler? Though we didn’t do it, you could find the slow parts of your code and add more profiling calls nested inside your original calls! Beautiful! In most languages, I would have to explicitly add that timing code (the stuff in profile) around every section I wanted to time. What a hassle. In Ruby, however, I get to keep it all in one place and (more important) out of my way!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.78.102