From: Chris Pine Date: 2003-04-17T13:38:37+09:00 Subject: Re: Newbie proc question ----- Original Message ----- From: "James Toomey" Can anyone help reduce my confusion? ---------------------------- Well, as I'm sure you have guessed, your examples weren't really great uses of blocks/procs. So what's the point of a proc? Well, it's a little function you can pass around. There are many times when you might want to do this. Basically, it's another level of abstraction. Let's look at some low-level abstractions and work our way up. Let's say you were writing a program where you wanted to say 'hello' three times in a row. You might do something like: puts 'hello' puts 'hello' puts 'hello' (We could have done a loop or something, but this is good enough for now.) But what if I wanted to do the same thing from another spot in the code? I could just copy and paste, but copy and paste is BAD. (*Cut* and paste is fine... just no copying.) No, the thing to do is to group this code together in a method: def threeHellos puts 'hello' puts 'hello' puts 'hello' end Now I can call this from any number of places in my code, no problem. I don't want duplicate code running around. Now what if I wanted to say 'goodbye' three times in other places in my code? I could define a new method `threeGoodbyes', but it would share a great deal in common with `threeHellos'. I should refactor: def sayItThreeTimes phrase puts phrase puts phrase puts phrase end def threeHellos sayItThreeTimes 'hello' end def threeGoodbyes sayItThreeTimes 'goodbye' end Excellent. But what if, later on, I want to do something else three times? Like get a string from user input and echo it? def threeEchos puts gets puts gets puts gets end It certainly has something in common with `sayItThreeTimes', but how do we factor it out? We use procs. After some more refactoring: def doItThreeTimes aProc aProc.call aProc.call aProc.call end def threeEchos echoProc = proc {puts gets} doItThreeTimes echoProc end def sayItThreeTimes phrase sayItProc = proc {puts phrase} doItThreeTimes sayItProc end See? Now we are passing in the proc as a normal parameter. Ruby has a special way to pass in one proc per method call, though, using `&': def doItThreeTimes (&aProc) aProc.call aProc.call aProc.call end def threeEchos echoProc = proc {puts gets} doItThreeTimes (&echoProc) end def sayItThreeTimes phrase sayItProc = proc {puts phrase} doItThreeTimes (&sayItProc) end Ruby also has a way to pass that proc in using a block, like this: def threeEchos doItThreeTimes {puts gets} end def sayItThreeTimes phrase doItThreeTimes {puts phrase} end It means the same thing; it's just that we don't have to give it a name. Similarly, if we want, we don't have to give it a name in `doItThreeTimes', either: def doItThreeTimes yield yield yield end This also means the same thing: just call the special proc we passed in. Of course, this was a silly example; we don't need `doItThreeTimes' when we have this: 3.times { ... } `n.times' takes a block and calls it `n' times. So that's one common use of blocks: iterators. `each' is another popular method which takes a block. I assume you are familiar with it? Blocks are also useful when you have something you want done before and after some code is called. Let's say you were writing a profiler. To find out how long a method call takes, you must find out the time before you call the method, then the time after it is done, and subtract them: startTime = Time.now # Put your code to profile here. callTime = Time.now - startTime Maybe you do this often, and you find yourself copying and pasting... nonono! You can use blocks: def profile startTime = Time.now yield # Call the code to be profiled. Time.now - startTime end profile {12345**12345} You can use this for logging, database transactions... This is often used for file IO, since you want to open a file, do some stuff to it, then close it at the end. If you use a block, you don't have to bother with closing it -- Ruby will do it for you. So does that make a little more sense? Chris