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 objectsBlock 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
lambdaor 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) # => 7Compare 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
end
It'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