# Rational number objects in Perl using closures

# This time, we'll use a Perl hash able instead of another function
# as the "interface" to our objects.


sub makerat
{
    my ($n,$d) = @_;
    ($d!=0) || die "denominator can't be zero";
 
  # define methods
  my ($gcd,$eq,$mult,$tostring);

  # Euclid's algorithm - this should be considered private
  $gcd =    sub {
                  my ($a,$b) = @_;
                 if ($a==0) {$b} else {$gcd->($b%$a,$a)}
                };
 
  my $g = $gcd->($n,$d);   # reduce fraction
  $n = $n/$g;   $d = $d/$g;
  
  # rational equality
    $eq = sub { 
            	my $B = $_[0];
		($n * $B->{denominator} == $d*$B->{numerator});
	      };

    $tostring = sub { $n."/".$d };

    $mult = sub {
	my $B = $_[0];
        makerat($n*$B->{numerator}, $d*$B->{denominator})
    };
  
  # return interface as a hash table
    {
      numerator => $n,   
      denominator => $d,
      tostring => $tostring,
      eq => $eq,
      mult => $mult
    }

} # makerat

# Note that by inserting the numerator and denominator into a hash table,
# They effectively become "public" - anyone can access and modify them as
# they do any other hash table entry.  To make them read-only, you
# will still need to write "accessor" functions to encapsulate them as
# variables inside function closures.

$a = makerat(2,6);
$b = makerat(1,3);
print $a->{numerator}, "\n";
print $a->{tostring}->(), "\n";
if ($a->{eq}->($b)) {print "equal\n"} else {print "not equal\n"}
$c = $a->{mult}->($b);
print $c->{tostring}->(), "\n";

