bembe0417 has asked for the wisdom of the Perl Monks concerning the following question:

Hi,
I inherited a script that does calculations on a
comma-seperated value file.

This file contains the following:
Australian (Dollar),AUD,0.5295,1.8896
Canadian (Dollar),CAD,0.6295,1.5888
German (Mark),DEM,0.8794,1.1375
Spanish (Peseta),ESP,0.8794,1.1375
Euro,EUR,0.8794,1.1375
French (Franc),FRF,0.8794,1.1375
British (Pound),GBP,1.4316,0.6987
Hong Kong(Dollar),HKD,0.1282,7.7997
Italian (Lira),ITL,0.8794,1.1375
Japanese (Yen),JPY,0.007592,131.73
Dutch (Guilder),NLG,0.8794,1.1375
New Zealand (Dollar),NZD,0.4375,2.2883

I want the script to generate the output like this:
AUD,USD,08-APR-02,08-APR-02,.5308
CAD,USD,08-APR-02,08-APR-02,.6276
GBP,USD,08-APR-02,08-APR-02,1.4339
JPY,USD,08-APR-02,08-APR-02,.007556
NZD,USD,08-APR-02,08-APR-02,.4405
EUR,USD,08-APR-02,08-APR-02,.8776
EUR,GBP,08-APR-02,08-APR-02,.6120
DEM,USD,08-APR-02,08-APR-02,.4443
FRF,USD,08-APR-02,08-APR-02,.1325
NLG,USD,08-APR-02,08-APR-02,.3943
DEM,GBP,08-APR-02,08-APR-02,.3137
FRF,GBP,08-APR-02,08-APR-02,.0935
NZD,AUD,08-APR-02,08-APR-02,.822719293284751


So far the only output that I am getting looks like this:
AUD,USD,10-APR-02,10-APR-02,0
CAD,USD,10-APR-02,10-APR-02,0
DEM,USD,10-APR-02,10-APR-02,0
FRF,USD,10-APR-02,10-APR-02,0
GBP,USD,10-APR-02,10-APR-02,0
JPY,USD,10-APR-02,10-APR-02,0
NZD,USD,10-APR-02,10-APR-02,0
NLG,USD,10-APR-02,10-APR-02,0
Illegal division by zero at ./calculator line 214.
DEM,GBP,10-APR-02,10-APR-02,0
Illegal division by zero at ./calculator line 214.
FRF,GBP,10-APR-02,10-APR-02,0
Illegal division by zero at ./calculator line 214.
EUR,GBP,10-APR-02,10-APR-02,0
EUR,USD,10-APR-02,10-APR-02,0
Illegal division by zero at ./calculator line 214.
NZD,AUD,10-APR-02,10-APR-02,0
Illegal division by zero at ./calculator line 214.
ESP,GBP,10-APR-02,10-APR-02,0
Illegal division by zero at ./calculator line 214.
ITL,GBP,10-APR-02,10-APR-02,0


This is the script:
#!/usr/local/bin/perl # use strict; # my $tomorrows_time = time() + 86400; my $sec; my $min; my $hour; my $mday; my $mon; my $year; my $wday; my $yday; my $isdst; my $month_name; my $newer; my $newline; my $line; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($tom +orrows_time); $month_name=('JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OC +T','NOV','DEC')[$mon]; ### DOUBLE-DIGIFY ANY YEAR NUMBERS BELOW 10... if ($year > 99) { $year = $year - 100; if ($year < 10) { $year = "0$year"; } } ### DEFINE GLOBAL VARIABLES... my $date_stamp = "$mday-$month_name-$year"; my $currency_file = "newer.csv"; my %important_currencies = (); $important_currencies{"USD"} = 1; my %currency_abbreviations = (); ### ORACLE HAS SOME FANCY ABBREVIATIONS FOR CURRENCIES AND SUCH... %currency_abbreviations = ( "Australia Dollar" => "AUD", "Canada Dollar" => "CAD", "Germany Mark" => "DEM", "Spain Peseta" => "ESP", "France Franc" => "FRF", "Britain Pound" => "GBP", "Hong Kong Dollar" => "HKD", "Italy Lira" => "ITL", "Japan Yen" => "JPY", "New Zealand Dollar" => "NZD", "Netherland Guilder" => "NLG", "Euro" => "EUR" ); my $currency_abbreviations; my @required_conversions = qw( AUD:USD CAD:USD DEM:USD FRF:USD GBP:USD JPY:USD NZD:USD NLG:USD DEM:GBP FRF:GBP EUR:GBP EUR:USD NZD:AUD ESP:GBP ITL:GBP ); chomp (@required_conversions); ### PARSE THE CURRENCY FILE WHICH NEEDS TO BE IN CSV (Comma-Seperate +d Value) FORMAT open (FINRATES,"<$currency_file"); my @fin_data_holder = <FINRATES>; close (FINRATES); ### PARSE ENTRIES FROM THE FILE, AND ADD THE VALUES OF DESIRED CURRE +NCIES INTO A HASH foreach $line (@fin_data_holder) { ### BECAUSE WE WERE HUNGRY... chomp($line); ### ELIMINATE QUOTE MARKS... $line =~ s/"//g; ### SUBSTITUTE SPACES FOR ':'... $line =~ s/\s+/ /g; ### FOR DEBUGGING... # $line =~ s/\s/:/g; # print "$line\n"; ### SPLIT THE LINE INTO VARS WHICH WE CAN USE! my ($country,$whatever,$usd_equiv_today,$currency_per_usd_toda +y) = split(/,/,$line); ### FOR DEBUGGING... my $newline = "$country $whatever $usd_equiv_today $currency_p +er_usd_today"; ### FOR DEBUGGING... # print "||$country||$usd_equiv_today||$currency_per_usd_today|| +\n"; my $key_entry; my @keys_currency_abbreviations; ### GET THE LIST OF KEYS FROM THE CURRENCY_ABBREVIATIONS HAS +H... @keys_currency_abbreviations = keys %currency_abbreviations; ### ATTEMPT TO MATCH EACH CURRENCY TO THOSE WHICH HAVE ORACL +E ABBREVIATIONS... foreach $key_entry (@keys_currency_abbreviations) { if ($country eq $key_entry) { ### GET THE ABREVIATED CURRENCY-CODE... my $abbrev = $currency_abbreviations{$key_entry +}; ### ADD THE ABBREVIATED CODE AND VALUE TO A H +ASH $important_currencies{$abbrev} = $usd_equiv_tod +ay; #print "Testing: $important_currencies{$abbrev} +"; } } } ### CONVERT THE DESIRED CURRENCIES, AND OUTPUT INTO ORACLE-DESIRED F +ORMAT... my $conversion_entry; my $from_currency; my $to_currency; my $first_currency; my $second_currency; foreach $conversion_entry (@required_conversions) { my $currency_per_usd_today; my ($from_currency,$to_currency) = split(/:/,$conversion_entry) +; my $first_currency = $important_currencies{$from_currency}; my $second_currency = $important_currencies{$to_currency}; my $newrate = 0; ### AVOID ANY NASTY DIVISION-BY-ZERO ERRORS WITH AN EVAL... eval { my $newrate = ($first_currency / $second_currency); }; ### IF EVAL FAILS, LET'S PRINT THE ERROR... print $@; ### OUTPUT IN ORACLE-DESIRED FORMAT... print "$from_currency,$to_currency,$date_stamp,$date_stamp,$ne +wrate\n"; }
HEELLLP!

Thanks,
bembe0417

Edit by tye

Replies are listed 'Best First'.
Re: What is wrong with my calculations?
by Fletch (Bishop) on Apr 10, 2002 at 03:00 UTC
    my $newrate = 0; ### AVOID ANY NASTY DIVISION-BY-ZERO ERRORS WITH AN EVAL... eval { my $newrate = ($first_currency / $second_currency); }; ### IF EVAL FAILS, LET'S PRINT THE ERROR... print $@;

    You've declared a new lexical variable $newrate that only exists for the duration of the eval block (and hides the previously declared one in the enclosing scope).

    You also always print $@. Rather than using eval, probably what you really wanted was:

    my $newrate = 0; if( $second_currency == 0 ) { warn "Second currency was zero\n"; } else { $newrate = $first_currency / $second_currency; }
Re: What is wrong with my calculations?
by joealba (Hermit) on Apr 10, 2002 at 04:32 UTC
    Here are a few other pointers:
    # Save some typing when you can! Make your vars lexical where it make +s sense. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ +tomorrows_time); # Get the last two digits. Much cleaner and faster $year = substr($year,-2,2); # I like to use a variable to turn on/off the debug code my $debug = 1; # somewhere near the top print "Testing: $important_currencies{$abbrev}" if $debug; # It's SO EASY to check for a zero in your divisor, so don't pass up t +he free ride! :) my $newrate; if (!$second_currency) { print "HEY! Don't divide! You'll puke!\n"; } else { $newrate = ($first_currency / $second_currency); }
    Here's my own version of the code. I liked the problem, so I played around with it a bit. Hopefully it has a few tasty tidbits that will help you out with future homework assignments.. erm, programming dilemmas. :) It looks like you put in some good thought and effort into your code, so I'm happy to help out!
    my $tomorrows_time = time() + 86400; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ +tomorrows_time); my $month_name=('JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP', +'OCT','NOV','DEC')[$mon]; $year = substr($year,-2,2); $mday = sprintf("%02d", $mday); my $date_stamp = "$mday-$month_name-$year"; my %conversions = ( 'AUD' => qq(USD), 'CAD' => qq(USD), 'DEM' => qq(USD), 'FRF' => qq(USD GBP), 'GBP' => qq(USD), 'JPY' => qq(USD), 'NZD' => qq(USD), 'NLG' => qq(USD), 'DEM' => qq(GBP), 'EUR' => qq(USD GBP), 'NZD' => qq(AUD), 'ESP' => qq(GBP), ); my %currencies = ( 'USD' => { 'usd_equiv' => 1, 'currency_per_usd' => 1, } ); foreach (<DATA>) { my ($country,$abbrev,$usd_equiv,$currency_per_usd) = split /,/; next unless $usd_equiv && $currency_per_usd; $currencies{$abbrev}{usd_equiv} = $usd_equiv; $currencies{$abbrev}{currency_per_usd} = $currency_per_usd; } foreach my $currency (keys %conversions) { foreach my $comp_currency (split /\W+/, $conversions{$currency}) { print qq($currency,$comp_currency,$date_stamp,$date_stamp,); print sprintf("%f\n", ($currencies{$currency}{'usd_equiv'} / $ +currencies{$comp_currency}{'usd_equiv'})); } } __DATA__ Australian (Dollar),AUD,0.5295,1.8896 Canadian (Dollar),CAD,0.6295,1.5888 German (Mark),DEM,0.8794,1.1375 Spanish (Peseta),ESP,0.8794,1.1375 Euro,EUR,0.8794,1.1375 French (Franc),FRF,0.8794,1.1375 British (Pound),GBP,1.4316,0.6987 Hong Kong(Dollar),HKD,0.1282,7.7997 Italian (Lira),ITL,0.8794,1.1375 Japanese (Yen),JPY,0.007592,131.73 Dutch (Guilder),NLG,0.8794,1.1375 New Zealand (Dollar),NZD,0.4375,2.2883
Re: What is wrong with my calculations?
by trs80 (Priest) on Apr 10, 2002 at 04:39 UTC
    Just wanted to commend a new monk for a good post. This is a good post because it contains the code and they used strict. It's commented to boot.
      Update: Thanks tye, for the code tag edit :)

      Hmmm, your points are valid, however there should have been code tags and arguably a readmore :)

      Reading Perl Monk Procedures and Writeup Formatting Tips would have told this monk this.

      While I comend your compliments, they should have included a couple tips like this to help the newbie write even better posts in the future, helping both the newbie and those who are reading the post.

      "Nothing is sure but death and taxes" I say combine the two and its death to all taxes!