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.

Posted in ,  | Tags , , , , , , , ,

blog comments powered by Disqus