Proc (short for procedure) is a block of code, bound to a variable (closely related to a block, discussed here). As is always the way in Ruby, a Proc is an object, and so can be created with the new method, though, as discussed later, it is generally preferable to use the lambda method. Here are some examples (expanding on those in the Ruby documention):# A Proc can have one, none or many arguments
times7 = Proc.new {|n| n * 7 }
statement = Proc.new { 'from statement' }
multiply3 = Proc.new {|x, y| x * y }
The code in the Proc object can be invoked by using the call method.
p times3.call(12) #=> 36
p times5.call(5) #=> 25
p times7.call(8) #=> 56
p times3.call(times5.call(4)) #=> 60
p statement.call #=> "from statement"
p multiply.call(4, 3) #=> 12
You can pass around Proc objects like any other object:
def gen_times(factor)
return Proc.new {|n| n*factor }
end
class ProcTest
# Class variable is a Proc
@@times13 = Proc.new {|n| n * 13 }
# Method uses various Proc objects
def test
times11 = Proc.new {|n| n * 11 }
p times11.call(3)
p @@times13.call(7)
times2 = gen_times(2)
p times2.call(19)
end
# Method uses a Proc passed as an argument
def test_argument prc
p prc.call(4)
end
end
# ProcTest object instantiated, and methods called
pt = ProcTest.new
pt.test
pt.test_argument Proc.new {|x| x + 5}
Instead of using call, you can invoke the code using square brackets notation. The following are equivalent:
multiply.call(4, 3)
multiply[4, 3]
Proc.new vs lambda
If you call a Proc with too few arguments, Ruby will pad them out with the nil object, so multiply.call(14) above would invoke the Proc code with 14 and nil (which would generate an error in this case). Any extra arguments are simply discarded.
The
Kernal has a method lambda (there is also a method proc, which reportedly does the same as Proc.new, but I found it identical to lambda) which will also give a Proc object, but in this case the Proc will raise an ArgumentError is the argument count is wrong.count_nils_new = Proc.new {|x, y, z|
"#{x.nil?} #{y.nil?} #{z.nil?}"
}
count_nils_lambda = lambda {|x, y, z|
"#{x.nil?} #{y.nil?} #{z.nil?}"
}
# The 4 is quietly discarded
p count_nils_new.call(1, 2, 3, 4)
# The method is sent 1, 2, nil
p count_nils_new.call(1, 2)
# The method is sent nil, nil, nil
p count_nils_new.call
# This is fine
p count_nils_lambda.call(1, 2, 3)
# These will all generate an ArgumentError
p count_nils_lambda.call(1, 2, 3, 4)
p count_nils_lambda.call(1, 2)
p count_nils_lambda.callProc objects have an arity method that can be used to determine how many arguments the Proc is expecting.Using return in a Proc
Using an explicit
return in a Proc object created with Proc.new (but not lambda) will cause the calling method to return that value.def with_return_and_new
prc = Proc.new { return 'This is printed' }
prc.call
'Never seen'
end
def no_return_with_new
prc = Proc.new { 'no_return_with_new' }
prc.call
'This is printed'
end
def with_return_and_lambda
prc = lambda { return 'with_return_and_lambda' }
prc.call
'This is printed'
end
p with_return_and_new
p no_return_with_new
p with_return_and_lambda
It seems that the
Proc object is returning not just the value, but the return command too. In general, therefore, it is a bad idea to use an explicit return inside a Proc object defined with Proc.new (if only because the effect will be confusing to anyone with out a good understanding of Ruby peculiarities). This is discussed more here:http://innig.net/software/ruby/closures-in-ruby.rb
API for the Proc object:
http://www.ruby-doc.org/core/classes/Proc.html
See also:
http://eli.thegreenplace.net/2006/04/18/understanding-ruby-blocks-procs-and-methods/
http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html
Struggling with Ruby: Contents Page
5 comments:
Thanks for going over this. I didn't quite 'get it' when reading through the Pick-axe.
Your examples and some time in irb helped.
Jeremy
Thanks, that's great.
Very helpful post.
times7 = Proc.new {|n| n * 7 }
p times7.call(12) #=> 36
p times7.call(5) #=> 25
p times7.call(8) #=>56
are the method calls with respect to the Below mentioned post correct..
p times3.call(12) #=> 36
p times5.call(5) #=> 25
p times7.call(8) #=> 56
when i executed the code in the block...the code gave some error.
Thanks for the post, its really helpful. Got much clear idea about procs.
Post a Comment