1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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 $_;
}
|