views:

122

answers:

5

Hi there,

I have started learning Ruby, and I have read a couple of tutorials and I even bought a book ("Programming Ruby 1.9 - The Pragmatic Programmers' Guide"), and I came across something new that I haven't seen before in any of the other languages I know (I am working as a PHP webdeveloper).

Blocks & Procs. I think I understand what they are, but what I don't understand is why they are so great, and when and why I should use them. Everywhere I look they say blocks and procs are a great feature in Ruby, but I don't understand them.

Can anybody here give a total Ruby-newbie like me some explanations?

A: 

These concepts are tied to concepts from functional programming into ruby, hence it enables you to use patterns and techniques that are usually found in languages, where functions are first-class citizens.

flq
Not really, blocks in Ruby come from Smalltalk and in both languages they are objects not functions, although in both, they look and act just like anonymous functions. eg. Even the syntax are very alike. For instance in smalltalk a block would be `a := [:x|x+1]`
OscarRyz
@OscarRyz: I'm kind of picking nits here, but blocks in Ruby really are functions and *aren't* objects. That's why people say they're closures — closures are a kind of function. You can't, for example, write `totally_an_object = {|n| n * 7}` — that's a syntax error. You need to call `proc` with the block in order to get an actual object representing that function. Blocks were true objects in Smalltalk, but Ruby's version is slightly different
Chuck
+2  A: 

Blocks are used from many methods in Ruby classes, and they are used where in PHP you would use a callback.

[1,2,3,4,5].each {|i| print "#{i} "}

[1,2,3,4,5].each do |i|
  print "#{i} "
end

File.open('p014constructs.rb', 'r') do |f1|  
  while line = f1.gets  
    puts line
  end
end

PHP5 introduced anonymous functions; instead of using a callback, you could use an anonymous function.

echo preg_replace_callback('~-([a-z])~', function ($match) {
  return strtoupper($match[1]);
}, 'hello-world');
kiamlaluno
I understand blocks when using iterators and so on, but it is all the other stuff that I don't get. (Mostly procs, i guess)
Nilks
Add in the part about blocks being closures! (i.e. you can access variables outside of the block's scope, meaning you don't have to create a bunch of temp structures, etc)
Paul Betts
what does Ruby 1.8 blocks "dont have local variables" mean. I think you'll find you're not correct.
banister
+1  A: 

Its important to view blocks not as using methods to begin a code block, you are actually taking the block and using it like a parameter in the function.

So when you use the each method to iterate over an array like so:

superOverFlowArray.each { |flow| puts flow }

You are sending the block { |flow| puts flow } into the each method. The code is telling ruby to send the current value of the array to this block in place of |flow|.

Procs take a block and make them into a variable. Procs are object instances that hold blocks. Blocks are passed as a parameter to the proc and are executed when you call the 'call' method on that Proc instance.

So here is a Proc example:

def category_and_title(category)
     Proc.new { |title| "The title is: " + title + " in category: " + category }
end

myFirstTitle = category_and_title("Police Drama")
mySecondTitle = category_and_title("Comedy")

puts myFirstTitle.call("Law and Order")
puts mySecondTitle.call("Seinfeld")
puts myFirstTitle.call("CSI")

The Proc will remember the original category that was passed in, allowing a convenient way of grouping types.

krio
+9  A: 

There are a lot of things that are good about blocks. The elevator pitch: Blocks let us pass around actions the same way we normally pass around data.

The most obvious level is that they let you abstract things out into functions that wouldn't be possible otherwise. For example, let's look at a common case where you have a list of things and you want to filter it to only include items that match some criterion:

int list[50] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
int evenNumbers[50] = {0};
int copyIndex = 0;
for (int i = 0; i < 50; i++) {
    if (list[i] % 2 == 0) {
        evenNumbers[copyIndex++] = list[i];
    }
}

Here's how you write that in Ruby:

list = 1..50
listCopy = list.select {|n| n.even?}

All the common busywork is moved out of your code and into a method with a meaningful name. We don't care about copying the array and going through indexes and all that — we just want a filtered list. And that's what select gives us. The block allows us to pass our custom logic into this standard method.

But iterators aren't the only place where this "hole in the middle pattern" is useful. For example, if you pass a block to File.open, it will open the file, execute the block with the file and then close the file for you.

Another thing that blocks give us is a really powerful form of callbacks. For example, without blocks, we might have to do something like this (based on how dialogs actually work in Objective-C Cocoa):

class Controller
  def delete_button_clicked(item)
    item.add_red_highlight
    context = {:item => item}
    dialog = Dialog.new("Are you sure you want to delete #{item}?")
    dialog.ok_callback = :delete_OK
    dialog.ok_receiver = self
    dialog.cancel_callback = :cancel_delete
    dialog.cancel_receiver = self
    dialog.context = context
    dialog.ask_for_confirmation
  end

  def delete_OK(sender)
    delete(sender.context[:item])
    sender.dismiss
  end

  def cancel_delete(sender)
    sender.context[:item].remove_red_highlight
    sender.dismiss
  end
end

Yowza. With blocks, we could do this instead (based on a common pattern used in many Ruby libraries):

class Controller
  def delete_button_clicked(item)
    item.add_red_highlight
    Dialog.ask_for_confirmation("Are you sure you want to delete #{item}?") do |response|
      response.ok { delete item }
      response.cancel { item.remove_red_highlight }
    end
  end
end

That's actually two levels of blocks — the do...end block and the two {}-style blocks. But it reads pretty naturally, doesn't it? This works because a block captures the context it's created in, so we don't need to pass around self and item.

As for Procs, they're just an object wrapper for blocks. Not very much to them.

Chuck
A: 

Blocks and procs let you extract small bits of code without the full overhead and complexity of methods.

Even map by itself is pretty powerful, and jQuery is built around this same concept:

  ['spite','rest','purpose'].map {|s| s << 'ful' }

And you can throw logic in if necessary

  ['sprite','restful','luck'].map {|s| s << (s.end_with?('uck') ? 'i' : '') << 'ly' }

There is a clever operator '&' that means "convert the symbol to a proc and call it", so you can convert objects strings:

  ['spite',12,:purpose].map(&:to_s)

And using closures and combining writing a simple closures we can find where adjacent of numbers appear. This is rather clumsy Ruby, but more concise than most languages:

  last = -2 # this variable is accessed and changed within the closure
  objs.sort.map do |o|
    if (last + 1) != o
       last = o
       nil # not one more than previous so return nil
    else
       o
    end
  end.compact  # compact removes nils

When you start rewriting it in another language you realize how convenient this is. It encourages you to structure your code into smaller operations, so it is more re-usable and testable.

ndp