CSC/ECE 517 Fall 2012/ch1 1w4 aj: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 94: Line 94:
By calling <code>Proc.new</code> by associating it with a block<br>
By calling <code>Proc.new</code> by associating it with a block<br>
<pre>obj = Proc.new { "a block" }  
<pre>obj = Proc.new { "a block" }  
obj
obj   #=> #<Proc:0x0a5e5a@/tmp/prog1.rb:1>
=>   #<Proc:0x0a5e5a@/tmp/prog1.rb:1>
</pre>
</pre>


Line 126: Line 125:
</pre>
</pre>


In the above example, the return_closure method returns a lambda that accesses local variable local_var, instance variable instance_var, class variable class_var and constant <code>CONST</code>. Even if the block is not in the scope of the object that contains these values, it is still accessible via the closure. So, when we update these values calling the update_values method, the values accessed via the closure also get updated.
In the above example, the return_closure method returns a lambda that accesses local variable <code>local_var</code>, instance variable <code>instance_var</code>, class variable <code>class_var</code> and constant <code>CONST</code>. Even if the block is not in the scope of the object that contains these values, it is still accessible via the closure. So, when we update these values calling the update_values method, the values accessed via the closure also get updated.
Closure is a very handy feature for Ruby developers and is used extensively. The rest of the chapter describes the implementation of Closures in different popular programming languages.
Closure is a very handy feature for Ruby developers and is used extensively. The rest of the chapter describes the implementation of Closures in different popular programming languages.
==Closures in C#==
A closure in C# can be done using Lambda expressions and delegates, where a first-class function (more info here) references the variables in the surrounding scope. Such a referenced variable is neither a parameter of the function nor a local variable.
For example,
<pre>
  static void Main(string[] args)
{
var obj = ClosureFunc(1);
Console.Writeline("Output 1= " + obj(2));
Console.Writeline("Output 2= " + obj(3));
}
public static Func<int, int> ClosureFunc(inc)
{
Func<int,int> SampleFunc = delegate(int val)
{
inc = inc + 1;
return val + inc;
};
return SampleFunc;
}   
</pre>
The result obtained is:
<pre>
Output 1= 4
Output 2= 6
</pre>
Now, when we call <code>ClosureFunc</code> from <code>Main()</code>,we get a method back that increments a local variable inside the method. <code>inc</code> is a local variable of <code>ClosureFunc()</code> which is accessed inside of the delegate. So, when we call the method twice, the local variable <code>inc</code> gets incremented twice outside of its original scope.
What actually happens here is the C# compiler encapsulates the delegate and the associated local variables into a compiler generated class. So, on invoking the delegate, it creates a lambda expression to increment the value of <code>inc</code> and results in calling a method on this class.
Another way to write the ClosureFunc is using <code>lambda ( => ) :</code>
  <pre>Func<string,string> ClosureFunc = val => val + inc;</pre>

Revision as of 21:21, 14 September 2012

Introduction

Code Blocks

Before understanding the concept of Closures, let us take a brief introduction on “Code Blocks” in Ruby. A code block is a chunk of code, Ruby statements and expressions written between curly braces { } or between “do…end”. For example:

 {  puts "Hello World!"  }

or

  do
     3.times(puts "Hello")
     object1.call
  end

Generally, as per Ruby standard, braces are used for single-line blocks and do...end for multiline blocks. Also, braces have a higher preference than do/end. A code block may appear only immediately after a method is invoked. If a method has parameters, then the block will look as follows:

 random_method("John") { puts "How you doing? " }

Closures in Ruby

What is a Closure?

Now, a block as shown before can use local variables from the surrounding scope. Such blocks are called Closures. Let us look at a simple example:

def closurefunc()
     lambda {|val| val + inc }
  end

  p1 = closurefunc(3)
  p1.call(1)    # => 4
 
  p2 = closurefunc(8)
  p2.call(5)   # => 13

In the above example, the value ‘3’ is assigned to the local variable inc of method closurefunc and value ‘1’ is assigned to inner variable val.

How does it work?

Let us look at a similar example, but this time, with strings.

 
def concat()
     lambda { |greet| greet + param }
  end

  p1 = concat("John")
  p1.call("Hello")    # => "HelloJohn"
 
  p2 = concat("Jim")
  p2.call("Good morning ")   # => "Good morning Jim"

The method concat returns a Proc object that references the method’s parameter, param. We need to use the call method of the Proc object to execute it. Even though the parameter param is out of scope when the block is called, the parameter is still accessible to the block. This is called a closure; where in the variables in the surrounding scope referenced in a block remain accessible for the life of that block and the life of any Proc object created from that block.


Why Closure is needed?

Other ways to use a closure

Rather than calling the method lambda, following ways can be used to create Proc objects:
Using the ( -> ) syntax
->params { . . . }
Example:
obj = ->(v1,v2) { v1 + v2 }
obj.call(2,3) # => 5

Note: There cannot be any space between > and opening paranthesis.

By passing a block to the method whose last parameter is prefixed with and ampersand That parameter will receive the block as a Proc object.

def sampmeth(v1, &block)
    puts block.inspect
 end
  sampmeth(1) { "a block" }
  sampmeth(3)

produces:

   #<Proc:0x0b5f5e@/tmp/prog1.rb:4>
  Nil

By calling Proc.new by associating it with a block

obj = Proc.new { "a block" } 
obj   #=>  #<Proc:0x0a5e5a@/tmp/prog1.rb:1>

Scope of a Closure

Closure remembers the context in which it is defined, and uses that context whenever it is called. The context may include the value of self, constants, local variables, class variables and any defined block.

class ClosureExample
  CONST = 1
  @@class_var = 4

  def return_closure
   local_var = 2
   @instance_var = 3
   lambda { [ CONST, local_var, @instance_var,  @@class_var, yield ] }
  end

  def update_values
    @instance_var += 1
    @@class_var += 1
  end
 end

 ex = ClosureExample.new 
 block = ex.return_closure { "dummy" }
 block.call    # => [ 1, 2, 3, 4, "dummy" ]
 ex.update_values
 block.call   # => [ 1, 2, 4, 5, "dummy" ]

In the above example, the return_closure method returns a lambda that accesses local variable local_var, instance variable instance_var, class variable class_var and constant CONST. Even if the block is not in the scope of the object that contains these values, it is still accessible via the closure. So, when we update these values calling the update_values method, the values accessed via the closure also get updated. Closure is a very handy feature for Ruby developers and is used extensively. The rest of the chapter describes the implementation of Closures in different popular programming languages.


Closures in C#

A closure in C# can be done using Lambda expressions and delegates, where a first-class function (more info here) references the variables in the surrounding scope. Such a referenced variable is neither a parameter of the function nor a local variable. For example,

  static void Main(string[] args)
 {
	var obj = ClosureFunc(1);
	Console.Writeline("Output 1= " + obj(2));
	Console.Writeline("Output 2= " + obj(3));
 }
 public static Func<int, int> ClosureFunc(inc)
 {
	Func<int,int> SampleFunc = delegate(int val)
		{
			inc = inc + 1;
			return val + inc;
		};
	return SampleFunc;
 }    

The result obtained is:

Output 1= 4
Output 2= 6

Now, when we call ClosureFunc from Main(),we get a method back that increments a local variable inside the method. inc is a local variable of ClosureFunc() which is accessed inside of the delegate. So, when we call the method twice, the local variable inc gets incremented twice outside of its original scope. What actually happens here is the C# compiler encapsulates the delegate and the associated local variables into a compiler generated class. So, on invoking the delegate, it creates a lambda expression to increment the value of inc and results in calling a method on this class. Another way to write the ClosureFunc is using lambda ( => ) :

Func<string,string> ClosureFunc = val => val + inc;