diff options
| -rwxr-xr-x | addemup | 125 |
1 files changed, 125 insertions, 0 deletions
@@ -0,0 +1,125 @@ +#!/usr/bin/perl -w + +use POSIX 'round'; +use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION++; + +$VERSION = "0.0.1"; +($SELF = $0) =~ s,.*/,,; + +sub VERSION_MESSAGE { + print "$SELF $VERSION\n"; +} + +sub HELP_MESSAGE { + print <<EOF; +$SELF - add up numbers in stdin, files, or command line args; print total. + +Usage: $SELF [-a] [-i] [-m] [args ...] + +By default, lines are read from standard input or the files given as +arguments. All numbers (integers, floating point, or hexadecimal with +leading 0x) found in the input are added together, and the total is +printed to stdout. + +Options: + -a Treats the arguments as numbers to add, rather than filenames. + -i Integers only. Any fractional part is truncated (use -r to round). + -m Also print the mean (average) of the numbers read. + -M Print only the mean (not the total). + -r Round to nearest integer (implies -i). + -h, --help + Print this usage message and exit. + -v, --version + Print version number and exit. + +Written by B. Watson <urchlay\@slackware.uk>, licensed WTFPL. +EOF +} + +# This regular expression doesn't recognize numbers without +# a digit before the decimal point, such as .01 or -.123. However, +# before this is used, the caller puts a 0 in the right place for +# such numbers. +# Doesn't the regex syntax with /x and comments remind you of +# assembly language? +$re = qr { + ( # EITHER: + (?: # hex number, + 0[xX] # leading 0x or 0X required, + [0-9a-fA-F]+ # hex digits, case insensitive. + ) + | # OR: + (?: # decimal number, + [-+]? # with optional leading sign, + \d+ # at least one digit required left of the decimal point, + (?:\.\d+)? # optional decimal point with trailing digits, + (?: # MAYBE: + [eE] # optional "e" or "E" for scientific notation, + [-+]? # optional exponent sign, + \d+ # at least one exponent digit, + )? # ...but the whole exponent part is optional! + ) + ) +}x; + +getopts('aihmMrv', \%opt) || exit 1; + +VERSION_MESSAGE(), exit(0) if $opt{v}; +HELP_MESSAGE(), exit(0) if $opt{h}; + +$opt{i} = 1 if $opt{r}; + +$sum = $count = 0; + +if($opt{a}) { + for(@ARGV) { + $sum += $_ for extract_numbers(); + } +} else { + while(<>) { + $sum += $_ for extract_numbers(); + } +} + +print $sum . "\n" unless $opt{M}; +if($opt{m} || $opt{M}) { + if($count) { + $avg = $sum / $count; + $avg = to_int($avg) if $opt{i}; + } else { + # always print an average, even if no numbers were read. + $avg = 0; + } + print $avg . "\n"; +} +exit 0; + +### +# Extract all the numbers in $_, return them an in array. +sub extract_numbers { + my @ret; + + # Handle numbers like .12 or -.99 by inserting a 0 before the . + s/(^|\D)(\.\d)/$1 . "0" . $2/ge; + + for(/$re/gc) { + $_ = hex $_ if /^0[xX]/; + $_ = to_int($_) if $opt{i}; + push @ret, $_; + $count++; + } + + return @ret; +} + +sub to_int { + warn "was: $_\n"; + if($opt{r}) { + $_ = round($_); + } else { + $_ = int $_; + } + warn "now: $_\n"; + return $_; +} |
