Procs, Lambdas and closure.
Posted by Sergey Enin 29 August 2011 at 22:13
We are all use blocks in our ruby programs, but what if we`d like to store somewhere or pass anonymous code to some method.
It is neccessary to do for example in Strategy pattern, where
1) we are defining or load some strategies;
2) we are passing one of strategy to object to call some action;
For that purpose Proc and lambda are very usefull.
Creating and calling
ruby-1.9.2-p290 :008 > is_positive = Proc.new{|x| x>0}
=> #< pro c:0x00000001236e40@(irb):8>
ruby-1.9.2-p290 :009 > is_negative = lambda{|x| x<0}
=> #< proc :0x0000000122b8b0@(irb):9>
ruby-1.9.2-p290 :010 > is_zero = ->(x){x==0}
=> #< proc :0x00000001222c10@(irb):10>
ruby-1.9.2-p290 :012 > is_positive.call(2)
=> true
ruby-1.9.2-p290 :013 > is_negative.call(2)
=> false
Composing with new ruby 1.9 technique
def compose(f,g)
->(x){f.call(g.call(x))}
end
check_sqrt = compose(->(x){Math.sqrt(x)}, ->(x){x*x})
=> #< proc :0x0000000125e238@(irb):30>
check_sqrt.call(2)
=> 2.0
check_sqrt_reverse = compose(->(x){x*x}, ->(x){Math.sqrt(x)})
=> #< proc :0x0000000123c7a0@(irb):30>
check_sqrt_reverse.call(2)
=> 2.0000000000000004
Difference in passing blocks and procs as block parameter:
data = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
data.sort{|x,y| x-y}
=> [1, 2, 3, 4, 5]
data.sort &->(x,y){(x-y).abs}
=> [5, 3, 1, 4, 2]
Additionally - you can invoke Proc object with:
check_sqrt[2]
=> 2.0
check_sqrt.(2)
=> 2.0
Arity shows amount of required params:
check_sqrt.arity
=> 1
Difference
The main difference between Proc and lambda in behaviouring in controll flow structures:
1) calling Proc is like calling block;
Also, if you will define
p = Proc.new {|x,y| puts x.inspect,y.inspect}
=> #< Proc :0x00000000e84518@(irb):48>
ruby-1.9.2-p290 :049 > p[1]
1
nil
ruby-1.9.2-p290 :050 > p[1,2]
1
2
ruby-1.9.2-p290 :051 > p[1,2,3]
1
2
2) calling lambda is like calling method, also params are processed like in method;
Closures
Closure is a conception when lambdas and Procs and retain or "closes over" the binding.
For example:
- in block -
ruby-1.9.2-p290 :052 > def mult(data, n)
ruby-1.9.2-p290 :053?> data.collect {|x| x*n}
ruby-1.9.2-p290 :054?> end
=> nil
ruby-1.9.2-p290 :055 > mult([1,2],3)
=> [3, 6]
- while in procs -
ruby-1.9.2-p290 :064 > def mult(n)
ruby-1.9.2-p290 :065?> ->(data){data.collect{|x| x*n}}
ruby-1.9.2-p290 :066?> end
=> nil
ruby-1.9.2-p290 :067 > multiply = mult(3)
=> #< Proc :0x000000012253e8@(irb):65 (lambda)>
ruby-1.9.2-p290 :068 > multiply.([1,2,3])
=> [3, 6, 9]
Consider that binding is not bounded statistically when the lambda, Proc is created.
That`s why we can use .binding method to evaluate code due to some binding.
ruby-1.9.2-p290 :073 > def mult(n)
ruby-1.9.2-p290 :074?> ->(x){x*n}
ruby-1.9.2-p290 :075?> end
=> nil
ruby-1.9.2-p290 :076 > doubler = mult(2)
=> #< Proc :0x000000010b0490@(irb):74 (lambda)>
ruby-1.9.2-p290 :077 > doubler[1]
=> 2
ruby-1.9.2-p290 :078 > eval("n=10",doubler.binding)
=> 10
ruby-1.9.2-p290 :079 > doubler[1]
=> 10
You can convert method to proc with: Object.new.method(:some_method) or with .to_proc
Additionally, you can use UnboundMethod to unbound method from object on which it is to be invoked.
ruby-1.9.2-p290 :092 > plus = Fixnum.instance_method("+")
=> #< UnboundMethod : Fixnum#+>
ruby-1.9.2-p290 :093 > plus_2 = plus.bind(2)
=> #< Method : Fixnum#+>
ruby-1.9.2-p290 :094 > plus_2[2]
=> 4
Thank you.

