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.call
Proc
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