#Krish, check this out:  

$facthash->{'$n:$n<=1'} = 1;
$facthash->{'$n:$n>1'} = '$n * $factorial->($n-1)';

sub mkfun  # mkfun takes a hash table pointer like $fact and returns a sub
  {
      my $fhash = $_[0];
      my @cles = (keys %$fhash);
      my @vals = (values %$fhash);
      my @paramvars; my ($var0, $bool0);  
      sub { my $indp;
	    my $match0 = 0;  # boolean 
            my $counter0 = 0;
	    my @mykeys; my $val; my @vars; my $bool;
            my $funbody = 'print "no matches\n"'; # body 
	    my $varform;  my $preamble;
	    @paramvars = @_;
	    # print "actual params: ", join ',',@paramvars, "\n";
	    while ((!$match0) && (counter <= $#vals))
	    {
	       if ($#paramvars > 0) 
	           { @mykeys = split / *, */, $cles[$counter0]; }
	       else
                   { $mykeys[0] = $cles[$counter0]; }
	       # print "mykeys are ", join ',',@mykeys; print "\n";
               $val = $vals[$counter0++];  # function body for this case
	       $bool = '1';  $preamble = '';  # identities
	       for ($indp=0;$indp<=$#paramvars;$indp++)
               { $key = $mykeys[$indp];  # e.g., '$n:$n>1' or '1'
		 $varform = split /:/, $key;  # form of parameter
		 if ($varform < 2) # not in name var:boolean form
		      { $vars[$indp]="\$paramvars[$indp]"; 
                        $bool0="\$paramvars[$indp] eq $key";
		      }
	         else { ($vars[$indp],$bool0) = split /:/, $key; 
			$preamble = 
			  $preamble."my $vars[$indp] = \$paramvars[$indp]; ";
                      }

		   if ($indp>0) {$bool0 =~ s{$var0}{\$paramvars[$indp-1]};}
		      # above line applies patterns previously seen.
	         $var0 = $vars[$indp]; # don't change original
	         $var0 =~ s/\$/\\\$/;
  	           $bool0 =~ s{$var0}{\$paramvars[$indp]};
	         $bool = $bool." and ($bool0)";
	         $val =~ s{$var0}{(eval $vars[$indp])};
	       } #for $indp loop
	         # print "vars is ", @vars, ", bool is ", $bool; print "\n";
	       if (eval $bool) 
		 { $match0 = 1;
		   $funbody = "$preamble  $val";
	           # print "funbody is: ", $funbody; print "\n";
		 } # if
            } # while more cases
	    eval $funbody;
	  }  # returned sub 
   }  # mkfun


$factorial = mkfun($facthash);
print "the factorial of 6 is ", $factorial->(6), "\n";

# Rules for defining functions: a parameter can either be a constant
# or a $ variable followed by a :boolean expression.  If a parameter
# is a variable by itself, it will be resolved to a constant.  Do not
# use any of the "my" variables in mksub - they should be considered 
# reserved names.  It is assumed that the user will define each case
# of the function as taking the same number of parameters.
# Also, in a parameter list, variables in the boolean expressions must
# already appear to the left of the ':'

# Fibonacci function:

$fib->{0} = 0;
$fib->{1} = 1;
$fib->{'$n:$n>1'} = '$fib->($n-1) + $fib->($n-2)';

$fib = mkfun($fib);

print "fib(8) is ", $fib->(8);  print "\n";


# Euclid's algorithm:

$gcd->{'0, $m:$m>0'} = '$m';
$gcd->{'$n:$n>0, $m:1'} = '$gcd->($m%$n,$n)';

$gcd = mkfun($gcd);

print "the gcd of 12 and 8 is ", $gcd->(12,8), "\n";


$largest->{'$n:1, $m:$m<$n'} = '$n';
$largest->{'$n:1, $m:$m>$n'} = '$m';
$largest = mkfun($largest);

print $largest->(3,5) + $largest->(5,2), "\n";

# Due to the nature of hashing, the different function cases should be
# mutually exclusive.
