Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
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
  • Outside of code tags, you may need to use entities for some characters:
            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 scrutinizing the Monastery: (12)
    As of 2014-12-17 20:48 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      Is guessing a good strategy for surviving in the IT business?





      Results (33 votes), past polls