开发者

I don't get the idea behind Ruby Proc... why not just use normal Method?

开发者 https://www.devze.com 2023-03-08 21:17 出处:网络
I guess the title says it. I\'m reading a book and I can see how they work but why would I create them instead of normal meth开发者_开发百科ods with normal parameters?

I guess the title says it. I'm reading a book and I can see how they work but why would I create them instead of normal meth开发者_开发百科ods with normal parameters?

I searched Google and SO I just got confused more.

Please clarify. Thanks.


A proc is different because you can store it in a variable. Therefore you can pass it as a paramater to a function, return it from a function, manipulate it, etc.

Procs, lambdas and blocks are one of the main things that make Ruby awesome.They are at the heart of Ruby's iterators for example. When you do something like:

collection.each do |item|
 //process item
end

you are basically passing a block (a Proc object) to the each function.

Let's say you a bunch of arrays, that you want to process in the same way. To save you from writing the each code every single time you can do something like:

handler = Proc.new{|item| do_something_with(item)}
array1.each &handler
array2.each &handler
....
arrayn.each &handler

When you want to pass a Proc to a function as a block, you have to preceed it with an &. The same goes for when you define a function that accepts a block parameter.

Another useful way to use Proc, is in functions.

Say you have a function that instantiates an object, does some minor changes, and returns it. To make it more flexible, you can make it accept a block like so:

def new_item(param1, param2, &block)
  my_item = Item.new
  my_item.attribute1 = param1
  my_item.attribute2 = param2
  yield my_item if block_given?\
  return my_item
end

Yield is where the magic happens. When that line is evaluated, the function will execute the block that you give it with my_item as a parameter. So you can do stuff like:

my_new_item = new_item(p1, p2) do |item|
  item.attribute3 = some_value
end

Now, my_new_item will have its attribute3 set as well as any other modification than you do in the block.

You don't use Procs and lambdas to replace functions, you use them to augment functions. You can have a function that returns a Proc that was built based on whatever parameters you give it. There are a lot of ways to be creative with Procs.


Procs can be passed around as objects and called when required. This is useful for modularity (amongst other things) and delayed processing. An example is the way ActiveRecord allows Procs in validations. Some examples:

validates_presence_of :admin_password, :if => Proc.new{|u| u.admin?}

In this case, the Proc is called (and reused) whenever the validation is carried out.


Procs - is just 'methods' with delayed call. You can save some code in proc and then execute it later. It is hard to explain where you can use them I'll try to get example from my projects.


A method is something concrete that you have to know how to invoke. Procs and blocks are arbitrary units of code that can be passed around and used at-will. You can think of a Proc as a sort of closure, I guess.

You use a Proc when your method needs help from whatever code is stored in the Proc. Maybe some logic to load a resource from somewhere, or something else non-trivial. Unlike a block, Procs are intended to be kept around, in a variable.


Good question, after answering on this question you will clearly know where and how to use Proc. For my understanding, the advantage of Proc is that you can pass the Proc as parameter to another methods. When you're defining the usual method, it bound to current context and can't be changed, you can't pass this method to another places. But with Proc you can do this. All iterators use this magic. You just saying what to do with every item in the array (you've created the proc object) and passing to iterator, and then iterator takes this method as an object and execute deep inside in his functionality.


Another common use is to simplify things that are structured as interpreters. Suppose you have a calculator of some sort, you could do it like this (which separates the association between the operator and its definition):

def add(a, b); a + b; end
def sub(a, b); a - b; end
#...
if(op == '+')
    add(a, b)
elsif(op == '-')
    sub(a, b)
#...
else
    raise 'Unknown operator'
end

Or you could use lambdas to make it a lot cleaner and tighter (i.e. pull the operator and its definition together to make it obvious what is going on):

ops = {
    '+' => lambda { |a, b| a + b },
    '-' => lambda { |a, b| a - b },
    #...
}
raise 'Unknown operator' if(!ops.has_key?(op))
ops[op].call(a, b)

The latter approach also makes special cases stand out more (IMHO).

A surprising number of things can be structured as interpreters of custom data structures once you're used to this sort of approach.

0

精彩评论

暂无评论...
验证码 换一张
取 消