def closure
x = "a_local_variable"
return proc { puts x } # when this line gets executed, the proc takes a snapshot
# of the embedding scope: in this case a local variable
end
closure.call # => a_local_variable
The bonus killer feature of ruby is to attach a block to a method call. Inside the method, it can be accessed like proc or with the yield keyword.
def each_number(&block) # special parameter gives the attached block
block.call 1
block.call 2
block.call 3
yield 4
end
x = 5
each_number {|i| puts x + i } # => 6 7 8 9
# block gets called 4 times
# it has access to local variable x
# and to block local variable i,
# passed as argument in each_number
each_number do |i| # do .. end is the same as { .. }
puts i
end
Note the block argument syntax similar to SmallTalk, which gives you a new block local variable, dying right after the block (except there was a local variable with same name before).
You can omit the &block parameter, when using the yield syntax. Also you can ask the interpreter, if there was a block attached with block_given? .
Ruby's blocks are fundamental to the language, use them as often you can. If you are doing list processing, your code will get small and very readable like. Look at the Enumerable methods, possibly the most used ones in the builtin classes.
Last thing I discovered was a little tweak to Enumerable like this.
module Enumerable
def _map(&block)
map {|i| i.instance_eval(&block) } # &block passes the proc to instance_eval
# like it was attached to the method call
end
end
["lower", "case", "words"]._map { upcase } # => [ "LOWER", "CASE", "WORDS" ]
In each iteration, the block gets executed in the object's scope, so you an access the instance methods directly and evilwise the instance variables, too.
Here's how to create a factory method that returns objects that perform an operation.
One possible solution (inspired by SingletonTutorial):
def op_gen(op_name, &block)
op = op_name.dup
# This comes from SingletonTutorial
class << op
self
end.instance_eval { define_method(:apply, &block) }
op
end
What we're doing here is retrieving a singleton class object, and defining a method on it. This is an interesting feature because essentially we're creating a class that is specific to only one object (op). Using instance_eval means that you're able to pull variables from the local scope (&block) and execute them in the context of the class.
It works works like this:
plus = op_gen("+") {|l, r| l + r}
plus.to_s #=> "+"
plus.apply(10, 15) #=> 25
pow = op_gen("^") {|l, r| l ** r}
pow.to_s #=> "^"
pow.apply(2, 5) #=> 32
(Thanks for the help!)