http://qs321.pair.com?node_id=876618

I'm having to buy a car, and didn't know how to compare them, so I whipped up a little thing this morning to compare cars. It could use a bit more (estimated repairs/month, etc), but it does what I want.

Warning: Long, hence the readmore tags, though I can't recall if readmore tags actually do any good on a "top level" node.

#!/usr/bin/perl # # How much does it cost to drive a car? # use strict; use warnings; use Data::Dumper; use Getopt::Long; my $fuel_gal = "2.80"; my $weekly_miles=300; my $rpt_weekly=0; my $rpt_fuel =0; my $result = GetOptions( "miles=i" => \$weekly_miles, "weekly" => \$rpt_weekly, "fuel" => \$rpt_fuel, "gas=s" => \$fuel_gal, ); ##### # STEP 1: Fill out the distances between the locations you typically # drive between. We'll build our routes from them. Only one # one direction is required, unless you want asymmetric routes. ##### my %Distances = ( Home => { Work => 18.1, Son=>12.4, School=>12.4, Grandma=>25.3 }, Work => { Son => 11.3, School => 12.8 }, ); fixup_Distances(); # Fill out the missing table entries ##### # STEP 2: Define your common routes ##### my %Routes = map { my ($name, @locations) = @$_; $name => { Route => \@locations } } ( # Route_Name List of locations [ qw( Morning Home Work ) ], [ qw( Morning_w_Son Home School Work ) ], [ qw( Evening Work Home ) ], [ qw( Evening_w_Son Work Son Home ) ], [ qw( Get_Son Home Son Home ) ], [ qw( Visit_Grandma Home Grandma Home ) ], ); ##### # STEP 3: Build your daily itinerary ##### my @Itinerary = ( # DAY => qw( LIST OF ROUTES YOU TAKE THAT DAY ) [ SUN => qw( Visit_Grandma ) ], [ MON => qw( Morning_w_Son Evening ) ], [ TUE => qw( Morning Evening_w_Son ) ], [ WED => qw( Morning_w_Son Evening ) ], [ THU => qw( Morning Evening ) ], [ FRI => qw( Morning Evening ) ], [ SAT => qw( Get_Son ) ], ); ##### # STEP 4: Compute your weekly minimum mileage ##### if ($rpt_weekly) { print "\nWEEKLY MILEAGE\n\n"; my $weekly_ttl=0; for (@Itinerary) { my ($day, @routes) = (@$_); my @res; my $ttl=0; for (@routes) { my $d = route_length($_); $ttl += $d; push @res, $_, $d; } printf "%s %6.2f mi (", $day, $ttl; $weekly_ttl += $ttl; while (@res) { my $name=shift @res; my $dist=shift @res; print "$name=$dist "; } print ")\n"; } printf "\nTTL %6.2f mi MINIMUM (misc trips)\n", $weekly_ttl; } ##### # STEP 5: Set your weekly mileage to something more reasonable # (account for grocery shopping, visiting friends, etc. ##### printf "\nComputing remainder of report using $weekly_miles mi/wk\n"; if ($rpt_fuel) { my @Gas_Prices = ( 2.50, 2.75, 2.80, 2.90, 3.00, 3.10, 3.25 ); my @MPGs = ( 20, 25, 28, 30, 33, 35, 40 ); print "Fuel costs (MPG / GalPrice)\n\n"; print "MPG "; printf "%6.2f ", $_ for @Gas_Prices; print "\n"; for my $mpg (@MPGs) { printf "% 3u: ", $mpg; for my $GP (@Gas_Prices) { printf "%6.2f ", $GP*$weekly_miles / $mpg; } print "\n"; } print "\n\n"; } ##### # STEP 6: Plug in the cars you want to compare ##### my %Cars = ( "Optima EX" => { Mileage => [ 26, 35 ], Insurance => 448.70, Price => 13998.0, }, "PT Cruiser" => { Mileage => [ 20, 28 ], Insurance => 368.14, Price => 10998.0, }, "Solstice 2006" => { Mileage => [ 23.1, 30.2 ], Insurance => 230.89, Price => 14888, }, "Solstice 2006b" => { Mileage => [ 23.1, 30.2 ], Insurance => 230.89, Price => 10995, }, "Crossfire 2005" => { Mileage => [ 22.9, 27.3 ], Insurance => 230.89, Price => 12870, }, "Corolla 2010" => { Mileage => [ 26, 34 ], Insurance => 420.52, Price => 13599, }, "Elantra 2010" => { Mileage => [26, 34 ], Insurance => 403.74, Price => 14998, }, "Fusion 2008" => { Mileage => [ 20, 28 ], Insurance => 390.62, Price => 15998, }, ); print <<EOHDR; Monthly cost of ownership (weekly mi=$weekly_miles, fuel=\$$fuel_gal/g +al) Car Price /mo Ins Fuel -------------------- ------ ------ ------ -------/------- EOHDR for my $car (sort keys %Cars) { my ($miMax, $miMin) = map { 4 * $fuel_gal * $weekly_miles / $_ } @{$Cars{$car}{Mileage}}[0,1]; my $cost = $Cars{$car}{Price}; my $cost_mo = $cost/(5*12); my $ins_mo = $Cars{$car}{Insurance}/6; my $min = $ins_mo+$cost_mo+$miMin; my $max = $ins_mo+$cost_mo+$miMax; printf "%-20.20s %6u% 7.2f % 6.2f % 7.2f/%7.2f: (%6.2f / %6.2f)\n +", $car, $cost, $cost_mo, $ins_mo, $miMin, $miMax, $min, $max; } #--------------------------------------------------------------------- +------- # Miscellaneous subroutines #--------------------------------------------------------------------- +------- sub fixup_Distances { # Fill in the missing %Distances entries by reversing $from # and $to in the table. for my $from (keys %Distances) { for my $to (keys %{$Distances{$from}}) { # Don't mirror leg if it's already declared # (allows for asymmetric routes) next if exists($Distances{$to}) and exists($Distances{$to}{$from}); $Distances{$to}{$from} = $Distances{$from}{$to}; } } } sub route_length { my $route = shift; die "Route '$route' not defined!\n" if ! exists $Routes{$route}; if (! exists $Routes{$route}{LEN}) { my $ar = $Routes{$route}{Route}; my $cur = shift @$ar; my $ttl = 0; while (my $next = shift @$ar) { $ttl += leg_distance($cur, $next); $cur = $next; } $Routes{$route}{LEN} = $ttl; } return $Routes{$route}{LEN}; } sub leg_distance { # Distance between any two locations my ($from, $to) = (@_); die "Unknown location '$from'!\n" unless exists $Distances{$from}; die "Unknown location '$to'!\n" unless exists $Distances{$from}{$t +o}; return $Distances{$from}{$to}; }

...roboticus