<?xml version="1.0" encoding="windows-1252"?>
<node id="269051" title="Re: (On Speed Diff, etc.) Closure on Closures (beta)" created="2003-06-25 19:12:39" updated="2005-08-07 09:55:38">
<type id="11">
note</type>
<author id="196557">
chunlou</author>
<data>
<field name="doctext">
I read somewhere that said something like:

&lt;pre&gt;
     Object is data wrapped in methods.
     Closure is subroutine wrapped in data.
&lt;/pre&gt;

Instead of talking about the philosophical differences first, I ran some benchmarks to compare closure and object, just for the fun of it. I have the following three results:&lt;br&gt;&lt;br&gt;

&lt;b&gt;Counter:&lt;/b&gt;
&lt;code&gt;
use strict;
use warnings;
use Benchmark qw(cmpthese);
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub closure_counter {
	my $count = shift;
	return sub { $count++ };
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{package obj_counter;
	sub new {bless {count=&gt;$_[1]};}
	sub count {return ($_[0]-&gt;{count})++}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
my $obj_counter = obj_counter-&gt;new(3);
my $closure_counter = closure_counter(3);
cmpthese(1000,
	{
		obj_counter=&gt;sub{$obj_counter-&gt;count()for 1..1000;},
		closure_counter=&gt;sub{$closure_counter-&gt;()for 1..1000;}
	}
);

__END__

Benchmark: timing 1000 iterations of closure_counter, obj_counter...
closure_counter:  2 wallclock secs ( 2.58 usr +  0.00 sys =  2.58 CPU) @ 387.60/s (n=1000)
obj_counter:  5 wallclock secs ( 5.22 usr +  0.00 sys =  5.22 CPU) @ 191.57/s (n=1000)
                 Rate     obj_counter closure_counter
obj_counter     192/s              --            -51%
closure_counter 388/s            102%              --
&lt;/code&gt;&lt;br&gt;

&lt;b&gt;Directory Iterator:&lt;/b&gt;
&lt;code&gt;
use strict;
use warnings;
use Benchmark qw(cmpthese);
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
use IO::Dir;
sub closure_dir_iter {
	my $dir = IO::Dir-&gt;new(shift);
	return sub {
		my $fl = $dir-&gt;read();
		$dir-&gt;rewind() unless defined $fl;
		return $fl;
	};
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{package obj_dir_iter;
	use IO::Dir;
	sub new {bless {dir=&gt;IO::Dir-&gt;new($_[1])};}
	sub iter {
		my $fl = $_[0]-&gt;{dir}-&gt;read();
		$_[0]-&gt;{dir}-&gt;rewind() unless defined $fl;
		return $fl;
	}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
my $obj_dir_iter = obj_dir_iter-&gt;new( "." );
my $closure_dir_iter = closure_dir_iter( "." );
cmpthese(500,
	{
		obj_dir_iter=&gt;sub{while(defined(my $f = $obj_dir_iter-&gt;iter())){print "$f\n";}},
		closure_dir_iter=&gt;sub{while(defined(my $f = $closure_dir_iter-&gt;())){print "$f\n";}}
	}
);


__END__

Benchmark: timing 2000 iterations of closure_dir_iter, obj_dir_iter...
obj_dir_iter:  1 wallclock secs ( 1.20 usr +  0.00 sys =  1.20 CPU) @ 1666.67/s (n=2000)
closure_dir_iter:  1 wallclock secs ( 1.10 usr +  0.00 sys =  1.10 CPU) @ 1818.18/s (n=2000)
                   Rate     obj_dir_iter closure_dir_iter
obj_dir_iter     1667/s               --              -8%
closure_dir_iter 1818/s               9%               --
&lt;/code&gt;&lt;br&gt;

&lt;b&gt;Turtle Graph&lt;/b&gt; (drawing Koch curve)&lt;b&gt;:&lt;/b&gt;
&lt;code&gt;
use strict;
use warnings;
use Benchmark qw(cmpthese);
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
use constant PI =&gt; 3.14159265359;
sub closure_turtle {
	my ($h, $xy) = (0, [[0],[0]]); 	# h = heading (0 - north, 90 - east, etc)
	return sub { 
		$h = $h + (shift || 0);		# accumulative turns in degree
		my $d = shift || 0;			# distance
		$xy-&gt;[0][scalar(@{$xy-&gt;[0]})] = $d*sin(PI*$h/180) + $xy-&gt;[0][$#{@{$xy-&gt;[0]}}]; 
		$xy-&gt;[1][scalar(@{$xy-&gt;[1]})] = $d*cos(PI*$h/180) + $xy-&gt;[1][$#{@{$xy-&gt;[1]}}]; 
		return $xy;
	};
}
sub closure_koch {
	my ($turtle, $d, $level) = @_ ;
	if ($level==0) {$turtle-&gt;(0,$d); return 1;} 
	$turtle-&gt;(  0,0); closure_koch($turtle,$d/3,$level-1);
	$turtle-&gt;(-60,0); closure_koch($turtle,$d/3,$level-1);
	$turtle-&gt;(120,0); closure_koch($turtle,$d/3,$level-1);
	$turtle-&gt;(-60,0); closure_koch($turtle,$d/3,$level-1);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{package obj_turtle;
	use constant PI =&gt; 3.14159265359;
	sub new {return bless({h=&gt;0,xy=&gt;[[0],[0]]});}
	sub rt {$_[0]-&gt;{h}=$_[0]-&gt;{h}+$_[1];}			# right turn by x degrees
	sub fd {										# forward by x points
		my ($h, $xy, $d) = ($_[0]-&gt;{h}, $_[0]-&gt;{xy}, $_[1]);
		$xy-&gt;[0][scalar(@{$xy-&gt;[0]})] = $d*sin(PI*$h/180) + $xy-&gt;[0][$#{@{$xy-&gt;[0]}}]; 
		$xy-&gt;[1][scalar(@{$xy-&gt;[1]})] = $d*cos(PI*$h/180) + $xy-&gt;[1][$#{@{$xy-&gt;[1]}}]; 
		$_[0]-&gt;{xy} = $xy; return $xy;
	}
}
sub obj_koch {
	my ($turtle, $d, $level) = @_ ;
	if ($level==0) {$turtle-&gt;fd($d); return 1;} 
	$turtle-&gt;rt(  0); obj_koch($turtle, $d/3,$level-1);
	$turtle-&gt;rt(-60); obj_koch($turtle, $d/3,$level-1);
	$turtle-&gt;rt(120); obj_koch($turtle, $d/3,$level-1);
	$turtle-&gt;rt(-60); obj_koch($turtle, $d/3,$level-1);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
my $obj_turtle = obj_turtle-&gt;new();
my $closure_turtle = closure_turtle();
cmpthese(100,
	{
		obj_turtle=&gt;sub{for(0..2){$obj_turtle-&gt;rt(120); obj_koch($obj_turtle,170,4);}},
		closure_turtle=&gt;sub{for(0..2){$closure_turtle-&gt;(120, 0); closure_koch($closure_turtle,170,4);}}
	}
);


__END__

Benchmark: timing 100 iterations of closure_turtle, obj_turtle...
closure_turtle:  6 wallclock secs ( 6.49 usr +  0.00 sys =  6.49 CPU) @ 15.41/s (n=100)
obj_turtle:  5 wallclock secs ( 4.99 usr +  0.00 sys =  4.99 CPU) @ 20.04/s (n=100)
                 Rate closure_turtle     obj_turtle
closure_turtle 15.4/s             --           -23%
obj_turtle     20.0/s            30%             --
&lt;/code&gt;&lt;br&gt;
&lt;br&gt;

Closure counter beat object's by ~100%; Closure directory iterator beat object's by ~10%; but object turtle beat closure's by ~30%.&lt;br&gt;&lt;br&gt;

Why the difference?&lt;br&gt;&lt;br&gt;

Going from empirical to completely academic and philosophical, closure has pretty good application in lambda calculus. Consider the following code (we use "%" place of "lambda"):

&lt;code&gt;
# IF = %b.(%x.(%y.(b x) y))
$IF = sub { 
	my $b = shift;
	sub { 
		my $x = shift;
		sub { 
			my $y = shift;
			$b-&gt;($x)-&gt;($y);
		}
	}
}

# TRUE = %x.(%y.x)
$TRUE  = sub { 
	my $x = shift;
	sub { 
		my $y = shift;
		$x;
	}
}

# FALSE = %x.(%y.y)
$FALSE = sub { 
	my $x = shift;
	sub { 
		my $y = shift;
		$y;
	}
}

print $IF-&gt;($TRUE)-&gt;("then")-&gt;("else");   # prints "then"
print $IF-&gt;($FALSE)-&gt;("then")-&gt;("else");  # prints "else"
&lt;/code&gt;

To verify algebraically

&lt;PRE&gt;        (IF TRUE A B)
&lt;/PRE&gt;

observe that

&lt;PRE&gt;        (%b.%x.%y.(b x y) %a.%b.a A B)
           (%x.%y.(%a.%b.a x y)   A B)
              (%y.(%a.%b.a A y)     B)
                  (%a.%b.a A B)
                     (%b.A   B)
                         A
&lt;/PRE&gt;

Likewise, (IF FALSE A B) gives us B. (Full discussion can be found &lt;a href="http://perl.plover.com/lambda/tpj.html"&gt;here&lt;/a&gt;.)&lt;br&gt;&lt;br&gt;

Is it a big deal? Well, set theory and mathematical logic may look naively useless (which after all only give us Incompleteness Theorem); or number theory or group/fields theory may seem trivial (where only comes some obscure Elliptic Curves for us to make highly unbreakable encryption).&lt;br&gt;&lt;br&gt;

Lisp, considered a functional language, where closure's considered a major technique, is often used to implement A.I. stuff--somewhere along the line, there come RegEx, Mathematica, and taken-for-granted stuff like that.&lt;br&gt;&lt;br&gt;

Is closure (and functional programming) for everyone? Not when a third of the bookshelves in Computer section in the major bookstores are stuffed with Java and OOP. It's hard to be Func-ie when almost everyone else is Ob-ing.&lt;br&gt;&lt;br&gt;

But then, if somebody could find good use of group theory, somebody could surely find meaningful application of closure and functional programming. It's just a matter of creativity and practice.</field>
<field name="root_node">
268891</field>
<field name="parent_node">
268891</field>
</data>
</node>
