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 10Hm...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
wantarray
everywhere 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.:-)