Parent Directory
|
Revision Log
- changed reduce so that it would always leave the denominator positive (so reducing 6/-10 gives -3/5) - added documentation line for reduce - added method pow which takes an integer argument, and returns the fraction raised to that power (throwing warnings for indeterminant forms or division by 0) - added doc line for pow
1 #!/usr/bin/perl -w 2 # 3 # Fraction object 4 # Keeps track of two variables- numerator and denominator. 5 # Has subroutines for basic arithmatic functions, for anything 6 # more complicated, it can return a scalar value of 7 # numerator/denominator. 8 # VS 7/20/2000 9 10 11 =head3 Fraction 12 13 This object is designed to ease the use of fractions 14 15 =head4 Variables and Methods 16 17 Variables 18 19 numerator #numerator of fraction 20 denominator #denominator of fraction 21 22 Arithmetic Methods #these will all accept a scalar value or 23 #another fraction as an argument 24 25 plus #returns the sum of the fraction and argument 26 minus #returns fraction minus argument 27 subtractFrom #returns argument minus fraction 28 divBy #returns fraction divided by argument 29 divInto #returns argument divided by fraction 30 times #returns fraction times argument 31 compare #returns <, =, or > for the relation of fraction to argument 32 33 pow #returns fraction raised to argument, a given integer power 34 35 36 Other methods 37 38 reduce #reduces to lowest terms, and makes sure denominator is positive 39 scalar #returns the scalar value numerator/denominator 40 print #prints the fraction 41 print_mixed #prints the fractionas a mixed number 42 print_inline #prints the fraction like this 2/3 43 44 45 =head4 Synopsis 46 47 The fraction object stores two variables, numerator and denominator. The basic 48 arithmatic methods listed above can be performed on a fraction, and it can return its own 49 scalar value for use with functions expecting a scalar (ie, sqrt($frac->scalar) ). 50 51 52 =cut 53 54 55 BEGIN { 56 be_strict(); 57 } 58 59 package Fraction; 60 61 62 my %fields = ( 63 numerator => undef, 64 denominator => undef, 65 ); 66 67 68 sub new { 69 70 my $class = shift; 71 my @input = @_; 72 my $num; 73 my $denom; 74 75 unless (@_ == 1 or @_ == 2) { 76 warn "Invalid number of arguments to create new Fraction. Use the form new Fraction(numerator, 77 denominator) or new Fraction(value) to send a single scalar."; 78 } 79 80 # if we've been given a scalar as input: 81 # this will ensure that the numerator is a whole number. If it is not, this will 82 # multiply by 10 until it is a whole number, keeping track of the appropriate denominator. 83 # The loop conditional checks that the difference between the number and its int value 84 # is less than .000000001, NOT that they are equal. Because of imprecisions with floating 85 # point numbers, checking for equality will NOT work in many cases. 86 87 if (@_ == 1) { 88 my $tempDenom = 1; 89 while ($input[0] - int($input[0]) > .000000001) {$input[0] *= 10; $tempDenom *= 10;} 90 $num = $input[0]; 91 $denom = $tempDenom; 92 } 93 94 else { $num = $input[0]; $denom = $input[1]; } 95 96 97 my $self = { 98 _permitted => \%fields, 99 numerator => $num, 100 denominator => $denom, 101 }; 102 103 bless $self, $class; 104 105 return $self; 106 } 107 108 sub AUTOLOAD { 109 my $self = shift; 110 111 my $type = ref($self) or die "$self is not an object"; 112 113 # $AUTOLOAD is sent in by Perl and is the full name of the object (i.e. main::blah::blah_more) 114 my $name = $Fraction::AUTOLOAD; 115 $name =~ s/.*://; #strips fully-qualified portion 116 117 unless ( exists $self->{'_permitted'}->{$name} ) { die "Can't find '$name' field in object of class '$type'";} 118 119 if (@_) { 120 return $self->{$name} = shift; #set the variable to the first parameter 121 } else { 122 return $self->($name); #if no parameters just return the value 123 } 124 } 125 126 sub DESTROY { 127 # doing nothing about destruction, hope that isn't dangerous 128 } 129 130 131 ################################################################################### 132 # Basic Arithmetic Methods 133 # Each returns a new Fraction appropriate to the operation 134 135 sub plus { 136 my $self = shift; 137 my $input = shift; 138 139 $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction"); 140 141 my $lcm = $self->lcm($self->{denominator}, $input->{denominator}); 142 my $scaleA = $lcm/$self->{denominator}; 143 my $scaleB = $lcm/$input->{denominator}; 144 145 my $num = $self->{numerator}*$scaleA + $input->{numerator}*$scaleB; 146 147 my $frac = new Fraction($num, $lcm); 148 $frac->reduce; 149 $frac; 150 } 151 152 sub minus { 153 my $self = shift; 154 my $input = shift; 155 156 $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction"); 157 158 my $lcm = $self->lcm($self->{denominator}, $input->{denominator}); 159 my $scaleA = $lcm/$self->{denominator}; 160 my $scaleB = $lcm/$input->{denominator}; 161 162 my $num = $self->{numerator}*$scaleA - $input->{numerator}*$scaleB; 163 164 my $frac = new Fraction($num, $lcm); 165 $frac->reduce; 166 $frac; 167 } 168 169 sub subtractFrom { 170 my $self = shift; 171 my $input = shift; 172 173 $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction"); 174 175 my $lcm = $self->lcm($self->{denominator}, $input->{denominator}); 176 my $scaleA = $lcm/$self->{denominator}; 177 my $scaleB = $lcm/$input->{denominator}; 178 179 my $num = $input->{numerator}*$scaleB - $self->{numerator}*$scaleA; 180 181 my $frac = new Fraction($num, $lcm); 182 $frac->reduce; 183 $frac; 184 } 185 186 sub divInto { 187 my $self = shift; 188 my $input = shift; 189 190 $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction"); 191 192 my $num = $input->{numerator}*$self->{denominator}; 193 my $denom = $input->{denominator}*$self->{numerator}; 194 195 my $frac = new Fraction($num, $denom); 196 $frac->reduce; 197 $frac; 198 } 199 200 sub divBy { 201 my $self = shift; 202 my $input = shift; 203 204 $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction"); 205 206 my $num = $self->{numerator}*$input->{denominator}; 207 my $denom = $input->{numerator}*$self->{denominator}; 208 209 my $frac = new Fraction($num, $denom); 210 $frac->reduce; 211 $frac; 212 } 213 214 sub times { 215 my $self = shift; 216 my $input = shift; 217 218 $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction"); 219 220 my $num = $self->{numerator}*$input->{numerator}; 221 my $denom = $self->{denominator}*$input->{denominator}; 222 223 my $frac = new Fraction($num, $denom); 224 $frac->reduce; 225 $frac; 226 } 227 228 sub pow { 229 230 my $self = shift; 231 my $input = shift; 232 233 if($input == 0) { # 0 power, always return 1 234 if($self->{numerator} == 0) { 235 warn "Indeterminant form, 0^0, in Fraction power"; 236 } 237 return new Fraction(1,0); 238 } 239 240 my ($n, $d); 241 242 if($input<0) { 243 $d = $self->{numerator}; 244 $n = $self->{denominator}; 245 if($d==0) { 246 warn "Computing 1/0 in Fraction"; 247 } 248 $input = -$input; 249 } else { 250 $n = $self->{numerator}; 251 $d = $self->{denominator}; 252 } 253 my $g = $self->gcd($n, $d); 254 if($d<0) {$g = -$g;} 255 $n /= $g; 256 $d /= $g; 257 258 return new Fraction($n**$input, $d**$input); 259 260 } 261 262 ######################################################################### 263 # Other User-Accessed Methods 264 265 266 # returns a string denoting relation-- < = or > 267 # a string is returned for ease of use in writing problems 268 sub compare { 269 my $self = shift; 270 my $input = shift; 271 272 $input = $input->scalar if (ref($input) eq "Fraction"); 273 274 my $relation = undef; 275 $relation = "<" if ($self->scalar < $input); 276 $relation = "=" if ($self->scalar == $input); 277 $relation = ">" if ($self->scalar > $input); 278 279 $relation; 280 } 281 282 283 # returns the scalar value of numerator/denominator 284 sub scalar { 285 my $self = shift; 286 my $scalar = $self->{numerator}/$self->{denominator}; 287 288 $scalar; 289 } 290 291 292 # reduces a fraction to lowest terms, and makes denominator positive 293 sub reduce { 294 295 my $self = shift; 296 my $gcd = $self->gcd($self->{numerator}, $self->{denominator}); 297 if($self->{denominator}<0) {$gcd = -$gcd;} 298 299 $self->{numerator} = $self->{numerator}/$gcd; 300 $self->{denominator} = $self->{denominator}/$gcd; 301 } 302 303 304 # standard print method. Outputs string containing fraction displayed (in math mode 305 # if needed). 306 sub print { 307 my $self = shift; 308 my $out; 309 310 # if it's a whole number, just print the number 311 if ($self->{denominator} == 1) { 312 $out = $self->{numerator}; 313 } 314 # positive fraction: print out in plain math mode 315 elsif ($self->scalar > 0) { 316 $out = "\\ensuremath{ \\frac{$self->{numerator}}{$self->{denominator}} }"; 317 } 318 # negative fraction: print out negative sign and then absolute value in 319 # fraction form, avoiding parenthesis around the negative portion. 320 else { 321 my $foo = -$self->{numerator}; 322 $out = "\\ensuremath{ -\\frac{$foo}{$self->{denominator}} }"; 323 } 324 325 $out; 326 } 327 328 # forces printing of a mixed number, if applicable. 329 sub print_mixed { 330 my $self = shift; 331 my $out; 332 333 # if it's not an improper, just pass on to the regular print method 334 if ($self->{numerator} < $self->{denominator} ) { $out = $self->print; } 335 336 # otherwise print out the mixed number strong. This does not alter the 337 # actual value of the fraction in any way. 338 else { 339 my $tempNum = $self->{numerator}; 340 my $tempDenom = $self->{denominator}; 341 my $coeff = int($tempNum/$tempDenom); 342 $tempNum = $tempNum % $tempDenom; 343 344 $out = "\\ensuremath{ -$coeff \\frac{abs($tempNum)}{abs($tempDenom)} }" if ($self->scalar < 0); 345 $out = "\\ensuremath{ $coeff \\frac{$tempNum}{$tempDenom} }" if ($self->scalar > 0); 346 $out = $coeff if ($tempNum == 0); 347 } 348 349 $out; 350 } 351 352 353 # prints fraction as 4 or 5/3 as needed 354 sub print_inline { 355 my $self = shift; 356 my $out; 357 358 # if it's a whole number, just print the number 359 if ($self->{denominator} == 1) { 360 $out = $self->{numerator}; 361 } 362 # print as 5/3 363 else { 364 $out = "$self->{numerator}/$self->{denominator}"; 365 } 366 367 $out; 368 } 369 370 # these methods are simply so that in a problem, the user may access the variables without 371 # worrying about braces, that is, use $frac->denominator instead of $frac->{denominator} 372 sub numerator { 373 my $self = shift; 374 return $self->{numerator}; 375 } 376 377 sub denominator { 378 my $self = shift; 379 return $self->{denominator}; 380 } 381 382 ######################################################################## 383 # Internal Methods 384 385 # Least Common Multiple 386 # Used in arithmatic methods to convert two fractions to common denominator 387 # takes in two scalar values and returns their lcm 388 sub lcm { 389 my $self = shift; 390 my $a = shift; 391 my $b = shift; 392 393 #reorder such that $a is the smaller number 394 if ($a > $b) { 395 my $temp = $a; 396 $a = $b; 397 $b = $temp; 398 } 399 400 my $lcm = 0; 401 my $curr = $b; 402 403 while($lcm == 0) { 404 $lcm = $curr if ($curr % $a == 0); 405 $curr += $b; 406 } 407 408 $lcm; 409 } 410 411 412 413 # Helper function for reduce 414 # takes in two scalar values and uses the Euclidean Algorithm to return the 415 # greatest common denominator 416 sub gcd { 417 418 my $self = shift; 419 my $a = abs(shift); #absolute values because this will yeild the same gcd, 420 my $b = abs(shift); #but allows use of the mod operation 421 422 if ($a < $b) { 423 my $temp = $a; 424 $a = $b; 425 $b = $temp; 426 } 427 428 return $a if $b == 0; 429 430 my $q = int($a/$b); 431 my $r = $a % $b; 432 433 return $b if $r == 0; 434 435 my $tempR = $r; 436 437 while ($r != 0) { 438 439 #keep track of what $r was in the last loop, as this is the value 440 #we will want when $r is set to 0 441 $tempR = $r; 442 443 $a = $b; 444 $b = $r; 445 $q = $a/$b; 446 $r = $a % $b; 447 448 } 449 450 $tempR; 451 } 452 453 454 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |