Methods That Take Procs

When we pass a proc into a method, we can control how, if, or how many times we call the proc. For example, let’s say we want to do something before and after some code is run:

def​ do_self_importantly some_proc
puts ​"Everybody just HOLD ON! I'm doing something..."
some_proc.call
puts ​"OK everyone, I'm done. As you were."
end
say_hello = Proc.new ​do
puts ​'hello'
end
say_goodbye = Proc.new ​do
puts ​'goodbye'
end
do_self_importantly say_hello
do_self_importantly say_goodbye
Everybody just HOLD ON! I'm doing something...
hello
OK everyone, I'm done. As you were.
Everybody just HOLD ON! I'm doing something...
goodbye
OK everyone, I'm done. As you were.

Maybe that doesn’t appear particularly fabulous…but it is. It’s all too common in programming to have strict requirements about what must be done when. Remember opening and closing a file? If you want to save or load a file, you have to open the file, write or read the relevant data, and then close the file. If you forget to close the file, Bad Things can happen. But each time you want to save or load a file, you have to do the same thing: open the file, do what you really want to do, and then close the file. It’s tedious and easy to forget. But with this trick, it’s not even an issue.

You can also write methods that will determine how many times (or even whether) to call a proc. Here’s a method that will call the proc passed in about half of the time and another that will call it twice:

def​ maybe_do some_proc
if​ rand(2) == 0
some_proc.call
end
end
def​ twice_do some_proc
some_proc.call
some_proc.call
end
wink = Proc.new ​do
puts ​'<wink>'
end
glance = Proc.new ​do
puts ​'<glance>'
end
maybe_do wink
maybe_do wink
maybe_do glance
maybe_do glance
twice_do wink
twice_do glance

These are some of the more common uses of procs that enable us to do things we simply could not have done using methods alone. Sure, you could write a method to wink twice, and you could do it with your left pinky! But you couldn’t write one to just do something twice.

Before we move on, let’s look at one last example. So far the procs we have passed in have been fairly similar to each other. This time they will be quite different, so you can see how much such a method depends on the procs passed into it. Our method will take some object and a proc and will call the proc on that object. If the proc returns false, we quit; otherwise, we call the proc with the returned object. We keep doing this until the proc returns false (which it had better do eventually, or the program will crash). The method will return the last non-false value returned by the proc.

def​ do_until_false first_input, some_proc
input = first_input
output = first_input
while​ output
input = output
output = some_proc.call input
end
input
end
build_array_of_squares = Proc.new ​do​ |array|
last_number = array.last
if​ last_number <= 0
false
else
# Take off the last number...
array.pop
# ...and replace it with its square...
array.push last_number*last_number
# ...followed by the next smaller number.
array.push last_number-1
end
end
always_false = Proc.new ​do​ |just_ignore_me|
false
end
puts do_until_false([5], build_array_of_squares).inspect
yum = ​'lemonade with a hint of orange blossom water'
puts do_until_false(yum, always_false)
[25, 16, 9, 4, 1, 0]
lemonade with a hint of orange blossom water

OK, so that was a pretty weird example, I’ll admit. But it shows how differently our method acts when given very different procs. (Do yourself a favor, and try that lemonade. Unbelievable.)

The inspect method is a lot like to_s, except the string it returns tries to show you the Ruby code for building the object you passed it. Here it shows us the whole array returned by our first call to do_until_false. Also, you might notice that we never actually squared that 0 on the end of that array, but since 0 squared is still just 0, we didn’t have to do this. And since always_false was, you know, always false, do_until_false didn’t do anything at all the second time we called it; it just returned what was passed in.

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

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