Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??

I had an assignment to write a C++ class to handle rational numbers and perform arithmetic operations on them. After completing that, I decided to write a similar version in Perl. It gave me a chance to give operator overloading a try, so I grabbed my 4th edition Camel book (pg 457).

I got everything working pretty well, but, I noticed the add and subtract functions were similar, along with the mult/divide functions. So, I wanted to trim some code lines by using the add function to handle the addition of a negative number, etc...

Also, I am welcome to comments regarding my code (or even my posting skills!). Since user input is in the form of strings: "3/4", 5/16", etc... the _make_frac internal function works, but the toggle to indicate whole numbers is not translating to my to_string, so I test the denominator, instead.

Here is the complete, "perytidy'd" code. It begins with main and the RatNum package is inline below it. This is just for fun, the real (C++) homework is done. heh

Update: I removed my variable declaration line ( habit from C++ coding, I suppose), and tried a new denominator checker.

Another Update: Ok, I've tried to see what I could do about reusing _add for the _sub function, but just could not get anything to work. I tried to simply call _add from within _sub using $x->_add($n1 + $n2), but it gave an error about not finding an overloaded string function (""). I found that overload was trying to fallback to trying to concatenate the two arguments.

I then wrote a simpler version of a package that had a single element hash, but I still could not get it to work. I read the overload documentation about undefining the fallback option for double-string, wrote a dummy _str function and overloaded " "" ", but I'm about ready to move on from this. I was hoping I could consolidate the functions, but maybe it is better left alone. Here are the two functions that seem to say: TMTOWTDI

Thanks!

_add / _sub snippet
### Overloaded addition operator sub _add { my ( $x, $y ) = @_; my ( $n_sum, $sum_str ); my ( $n1, $n2 ) = ( $x->{NUMER}, $y->{NUMER} ); my ( $d1, $d2 ) = ( $x->{DENOM}, $y->{DENOM} ); my $com_denom = ( $d1 == $d2 ) ? $d1 : $d1 * $d2; $n_sum = ( $d1 == $d2 ) ? $n1 + $n2 : do { $n1 = $n1 * ( $com_denom / $d1 ); $n2 = $n2 * ( $com_denom / $d2 ); $n1 + $n2; }; my $gcd = _simplify( $n_sum, $com_denom ); ( $n_sum, $com_denom ) = map { $_ /= $gcd } ( $n_sum, $com_denom ) +; $sum_str = ($com_denom) ? ( $n_sum == $com_denom ) ? "1" : "${n_sum}/${com_denom}" : "0"; return $sum_str; } ### Overloaded subtraction operator sub _sub { my ( $x, $y, ) = @_; my ( $n_diff, $diff_str ); my ( $n1, $n2 ) = ( $x->{NUMER}, $y->{NUMER} ); my ( $d1, $d2 ) = ( $x->{DENOM}, $y->{DENOM} ); my $com_denom = ( $d1 == $d2 ) ? $d1 : $d1 * $d2; $n_diff = ( $d1 == $d2 ) ? $n1 - $n2 : do { $n1 = $n1 * ( $com_denom / $d1 ); $n2 = $n2 * ( $com_denom / $d2 ); $n1 - $n2; }; my $gcd = _simplify( abs($n_diff), $com_denom ); ( $n_diff, $com_denom ) = map { $_ /= $gcd } ( $n_diff, $com_denom + ); $diff_str = ($com_denom) ? ( $n_diff == $com_denom ) ? "1" : "${n_diff}/${com_denom}" : "0"; return $diff_str; }
full code
#!/usr/bin/perl use strict; use warnings; my $command = 0; print "\nThis program will perform arithmetic operations on fractions. +\n"; print "Enter fractions like this: 3/4, 15/16, etc..., or \"exit\" to e +xit.\n"; while ( $command ne "Q" ) { print "\nEnter first fraction: "; chomp( my $frac01 = <> ); if ( $frac01 =~ /^\S+\/(\S+)$/ ) { print "Division by zero not allowed, starting over..." and red +o unless ( $1 ); } print "Enter second fraction: "; chomp( my $frac02 = <> ); if ( $frac02 =~ /^\S+\/(\S+)$/ ) { print "Division by zero not allowed, starting over..." and red +o unless ( $1 ); } exit if ( $frac01 eq "exit" || $frac02 eq "exit" ); my $f1 = RatNum->new($frac01); my $f2 = RatNum->new($frac02); while ( $command ne "Q" ) { print "\nChoose: (A)dd, (S)ubtract, (M)ultiply, (D)ivide\n"; print "\tTest for (E)quality, or (Q)uit\n"; print "Answer: "; chomp( $command = <> ); if ( $command eq "A" ) { my $total = RatNum->new( $f1 + $f2 ); print "\n" . $f1->to_string() . " + " . $f2->to_string() . " = " . $total->to_string() . "\n"; } elsif ( $command eq "S" ) { my $total = RatNum->new( $f1 - $f2 ); print "\n" . $f1->to_string() . " - " . $f2->to_string() . " = " . $total->to_string() . "\n"; } elsif ( $command eq "M" ) { my $total = RatNum->new( $f1 * $f2 ); print "\n" . $f1->to_string() . " * " . $f2->to_string() . " = " . $total->to_string() . "\n"; } elsif ( $command eq "D" ) { my $total = RatNum->new( $f1 / $f2 ); print "\n" . $f1->to_string() . " / " . $f2->to_string() . " = " . $total->to_string() . "\n"; } elsif ( $command eq "E" ) { my $is_equal = ( $f1 == $f2 ) ? q{ } : " not "; print "\n" . $f1->to_string() . " and " . $f2->to_string() . " are" . $is_equal . "equal.\n"; } elsif ( $command eq "Q" ) { last; } else { print "Command not recognized...\n"; } } print "Type (Q) again to exit or another character to start over: +"; chomp( $command = <> ); if ( $command eq "Q" ) { print "Goodbye..\n"; exit; } } exit; ### Rational Number Package ### package RatNum; use overload "+" => \&_add, "-" => \&_sub, "*" => \&_mul, "/" => \&_div, "==" => \&_eql; sub new { my $class = shift; my $frac = { NUMER => undef, DENOM => undef, WHOLE => undef, }; bless $frac, $class; $frac->_make_frac(@_); return $frac; } ### Split string and assign NUMER, DENOM, and WHOLE sub _make_frac { no warnings; # expecting occasional empties my $self = shift; my $frac_str = shift; my ( $n, $d, $w ) = map { split /\//, $_ } ( $frac_str ); ## Checks for fraction entries that can be simply "1" ( $n, $d, $w ) = ( $n, $d || 1, ( $d > 1 ) ? 0 : 1 ); ( $n, $d, $w ) = (1) x 3 if ( $n == $d ); # when "2/2" should equ +al "1" ## Checks for "-" in denominator and adjusts accordingly $n = -$n and $d = -$d if ( ( $n > 0 ) && ( $d < 0 ) ); ( $self->{NUMER}, $self->{DENOM}, $self->{WHOLE} ) = ( $n, $d, $w ); return; } ### Stringify object to fracion sub to_string { my $self = shift; my ( $n, $d, $w ) = ( $self->{NUMER}, $self->{DENOM}, $self->{WHOLE} ); return "0" unless ( $n && $d ); return $n if ( $w ); return $n . "/" . $d; } ### Overloaded addition operator sub _add { my ( $x, $y ) = @_; my ( $n_sum, $sum_str ); my ( $n1, $n2 ) = ( $x->{NUMER}, $y->{NUMER} ); my ( $d1, $d2 ) = ( $x->{DENOM}, $y->{DENOM} ); my $com_denom = ( $d1 == $d2 ) ? $d1 : $d1 * $d2; $n_sum = ( $d1 == $d2 ) ? $n1 + $n2 : do { $n1 = $n1 * ( $com_denom / $d1 ); $n2 = $n2 * ( $com_denom / $d2 ); $n1 + $n2; }; my $gcd = _simplify( $n_sum, $com_denom ); ( $n_sum, $com_denom ) = map { $_ /= $gcd } ( $n_sum, $com_denom ) +; $sum_str = ($com_denom) ? ( $n_sum == $com_denom ) ? "1" : "${n_sum}/${com_denom}" : "0"; return $sum_str; } ### Overloaded subtraction operator sub _sub { my ( $x, $y, ) = @_; my ( $n_diff, $diff_str ); my ( $n1, $n2 ) = ( $x->{NUMER}, $y->{NUMER} ); my ( $d1, $d2 ) = ( $x->{DENOM}, $y->{DENOM} ); my $com_denom = ( $d1 == $d2 ) ? $d1 : $d1 * $d2; $n_diff = ( $d1 == $d2 ) ? $n1 - $n2 : do { $n1 = $n1 * ( $com_denom / $d1 ); $n2 = $n2 * ( $com_denom / $d2 ); $n1 - $n2; }; my $gcd = _simplify( abs($n_diff), $com_denom ); ( $n_diff, $com_denom ) = map { $_ /= $gcd } ( $n_diff, $com_denom + ); $diff_str = ($com_denom) ? ( $n_diff == $com_denom ) ? "1" : "${n_diff}/${com_denom}" : "0"; return $diff_str; } ### Overloaded multiplication operator sub _mul { my ( $x, $y ) = @_; my ($n_prod) = ( $x->{NUMER} * $y->{NUMER} ); my ($d_prod) = ( $x->{DENOM} * $y->{DENOM} ); my $gcd = _simplify( $n_prod, $d_prod ); ( $n_prod, $d_prod ) = map { $_ /= $gcd } ( $n_prod, $d_prod ); my $prod_str = ( $n_prod == $d_prod ) ? "1" : "${n_prod}/${d_prod} +"; return $prod_str; } ### Overloaded division operator sub _div { my ( $x, $y ) = @_; my ($n_quot) = ( $x->{NUMER} * $y->{DENOM} ); my ($d_quot) = ( $x->{DENOM} * $y->{NUMER} ); ( $n_quot, $d_quot ) = ( -$n_quot, -$d_quot ) if ( $n_quot < 0 && $d_quot < 0 ); my $gcd = _simplify( $n_quot, $d_quot ); ( $n_quot, $d_quot ) = map { $_ /= $gcd } ( $n_quot, $d_quot ); my $quot_str = ( $n_quot == $d_quot ) ? "1" : "${n_quot}/${d_quot} +"; return $quot_str; } ### Checks for object equality sub _eql { my ( $x, $y ) = @_; return ( ( $x->{NUMER} == $y->{NUMER} ) && ( $x->{DENOM} == $y->{DENOM} ) ); } ### Simplify fraction using Euclid's algorithm sub _simplify { use integer; my ( $x, $y ) = @_; while ($y) { my $r = $x % $y; $r += $y if $r < 0; $x = $y; $y = $r; } return $x; } 1; # for whenever I make this a separate package file

In reply to Overloaded operators calling similar functions... by dbuckhal

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others pondering the Monastery: (7)
    As of 2015-07-31 02:49 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









      Results (274 votes), past polls