In computer science, a closure is a first-class function with free variables that are bound in the lexical environment. Blocks are closures, which means variables in the surrounding scope that are referenced in a block remain accessible for the life of that block and the life of and
Proc
object created from that block.Example:
def n_times(thing) lambda {|n| thing * n} end p1 = n_times(23) p1.call(3) # => 69 p1.call(4) # => 92
Compare with Python closures:
def generate_power_func(n): def nth_power(x): return x**n return nth_power end raised_to_4 = generate_power_func(4) raised_to_4(2)Blocks can be objects
Block can be converted to an object of class
Proc
. There are several ways where blocks are converted to objects.- If the last parameter in a method definition is prefixed with an ampersand (such as
&action
), Ruby looks for a code block whenever that method is called. - Use
lambda
or its alternative -> form. For example:
bo = lambda { |param| puts "You called me with #{param}" } bo.call 99 bo.call "cat" # produces: # You called me with 99 # You called me with cat lam = ->(p1, p2) { p1 + p2 } lam.call(4, 3) # => 7
Compare this with the "bound function operator" in CoffeeScript:callback = (message) => @voicement.push message
- Use
Proc.new
The
call
method on a proc object invokes the code in the original block.
The Symbol.to_proc trick
Ruby implements the
Ruby implements the
to_proc
for objects of class symbol.
names = %w{ant bee cat} result = names.map {|name| name.upcase} result = names.map {&:upcase}The last line means: apply the upcase method to each element of names. This works by relying on Ruby's type coercion. When you say
names.map(&xxx)
, you're telling Ruby to pass the Proc
object in xxx
to the map
method as a block. If xxx
isn't already a Proc
object, Ruby tries to coerce it into one by sending it a to_proc
message. If it was written Ruby, it would look something like this:def to_proc proc { |obj, *args| obj.send(self, *args) } # same as lambda endIt's an incredibly elegant use of coercion and of closures. However, the use of dynamic method invocations mean that the version of code that uses
&:upcase
is about half as fast as the more explicitly coded block. This doesn't matter so much unless in the performance-critical section of your code.
No comments :
Post a Comment