It's that time of year again -- drafting up an annual budget.
At first, I fired up dc to get some back-of-the-envelope numbers. But then I wasn't sure if I had done the math properly. Time to find a better tool.
But I hate Excel. Every time I use it, I feel like I spend more time fighting with the tool than actually getting stuff done. The solution? Why, Perl of course!
#!/usr/bin/perl -w
use strict;
my $total = 0; ## start with a clean balance
sub income ($$) {
my ($type, $amount) = @_;
$total += $amount;
printf "%20s: +%6d %7d\n", $type, $amount, $total;
}
sub expense ($$) {
my ($type, $amount) = @_;
$total -= $amount;
printf "%20s: -%6d %7d\n", $type, $amount, $total;
}
The rest of the script is self-documenting:
income "Yak Milking", 1000 * 12; ## 1 yak, $1000/month
expense "Hosting", 100 * 12;
expense "DSL", 70 * 12;
expense "Electricity", 100 * 12;
expense "PowerBook", 3500;
expense "Mac mini", 599 ## G4, 1.42 GHz
+ 425 ## 1GB RAM
+ 149 ## AppleCare
+ 58 ## Kbd + mouse
+ 999 ## 20" cinema display
;
expense "iPod", 500;
expense "iTunes Music Store", 30 * 12;
expense "ORA Books", 120 * 12;
expense "Jolt", 20 * 3 * 12; ## three cases / month
...as is the output:
$ perl expenses.pl
Yak Milking: + 12000 12000
Hosting: - 1200 10800
DSL: - 840 9960
Electricity: - 1200 8760
PowerBook: - 3500 5260
Mac mini: - 2230 3030
iPod: - 500 2530
iTunes Music Store: - 360 2170
ORA Books: - 1440 730
Jolt: - 720 10
Hm...maybe I better rethink this career in yak milking.
So after all that mumbojumbo you can now sayuse strict;
use warnings;
package Calc;
use base 'Exporter';
our @EXPORT = qw( income expense monthly );
sub income {
my ( $type, $amount ) = @_;
__PACKAGE__->new( $type, $amount );
}
sub expense {
my ( $type, $amount ) = @_;
__PACKAGE__->new( $type, -$amount );
}
sub monthly {
my $self = shift;
$self->{ amt } *= 12;
return $self;
}
sub new {
my $class = shift;
my ( $type, $amount ) = @_;
bless {
type => $type,
amt => $amount,
}, $class;
}
my $total = 0; # start with a clean balance
sub DESTROY {
my $self = shift;
$total += $self->{ amt };
my $sign = $self->{ amt } < 0 ? "+" : "-";
printf "%20s: %s%6d %7d\n", $self->{ type }, $sign, abs( $self->{ amt } ), $total;
}
1;
and you'll get the same output as you had.use Calc;
monthly income 'Yak Milking' => 1000;
monthly expense Hosting => 100;
expense PowerBook => 3500;
#...
Re:
ziggy on 2005-01-15T16:23:47
That's a really good idea. Here's a different approach:(OK, I admit it. I've been inhaling from the scheme pipe lately.no strict 'refs';
sub monthly ($$$) {
my ($function, $type, $amount) = @_;
&$function($type, $amount * 12);
}
sub quarterly ($$$) {
my ($function, $type, $amount) = @_;
&$function($type, $amount * 4);
}
sub biweekly ($$$) {
my ($function, $type, $amount) = @_;
&$function($type, $amount * 26);
}
sub weekly ($$$) {
my ($function, $type, $amount) = @_;
&$function($type, $amount * 52);
}
monthly income => "Yak Milking", 1000;
quarterly expense => "Hosting", 250;
biweekly expense => "Health Plan", 150;
weekly expense => "Dog Walking" => 100;;-) Re:
Aristotle on 2005-01-15T22:15:09
Yuck. I don't think the Schemers have much more appreciation for symrefs than the Perlistas.
:-) The difference is I wanted to be able to easily throw more qualifiers into the mix:
sub bi {
my $self = shift;
$self->{ amt }/= 2;
return $self;
}
# and later:
bi monthly expense Whatever => 100;It didn't start out OO. Initially I tried to just pass closures up the call chain. That didn't work too well though: I had to check
wantarrayeverywhere to do the printing. Can you say duplication? So I blessed the closure into a package with asub DESTROY { $$self->() }. Then I moved the printing into the DESTROY itself. And well, then I noticed it was much easier to stick the thing into a class, throw out all the repetition, and call it a day.:-)