0

I am trying to run this Perl program.

#!/usr/bin/perl
# ---------------     exchange.pl     -----------------

&read_exchange_rate;     # read exchange rate into memory

# now let's cycle, asking the user for input...

print "Please enter the amount, appending the first letter of the name of\n";
print "the currency that you're using (franc, yen, deutschmark, pound) -\n";
print "the default value is US dollars.\n\n";
print "Amount: ";

while (<STDIN>) {

  ($amnt,$curr) = &breakdown(chop($_));

  $baseval = $amnt * (1/$rateof{$curr});

  printf("%2.2f USD, ", $baseval * $rateof{'U'});
  printf("%2.2f Franc, ", $baseval * $rateof{'F'});
  printf("%2.2f DM, ", $baseval * $rateof{'D'});
  printf("%2.2f Yen, and ", $baseval * $rateof{'Y'});
  printf("%2.2f Pound\n\nAmount: ", $baseval * $rateof{'P'});
}

sub breakdown {
   @line = split(" ", $_);

   $amnt = $line[0];
   if ($#line == 1) {
     $curr = $line[1];
     $curr =~ tr/a-z/A-Z/;         # uppercase
     $curr = substr($curr, 0, 1);  # first char only
   } else { $curr = "U"; }
   return ($amnt, $curr);
}

sub read_exchange_rate {
  open(EXCHRATES, "

Whenever it gets to line 17 ($baseval = $amnt * (1/$rateof{$curr})), I get the error Illegal division by zero.

What's wrong?

I am new to Perl, so please explain your answer.

This only happens in Strawberry Perl. ActivePerl works, but it lists all the currency conversions as 0.0.

UPDATE: I changed the code to look like this:

#!/usr/bin/perl

&read_exchange_rate;     # read exchange rate into memory

# now let's cycle, asking the user for input...

print "Please enter the amount, appending the first letter of the name of\n";
print "the currency that you're using (franc, yen, deutschmark, pound) -\n";
print "the default value is US dollars.\n\n";
print "Amount: ";

while (<STDIN>) {

  ($amnt,$curr) = &breakdown(chomp($_));

  $baseval = eval { $amnt * (1/$rateof{$curr}) };

  printf("%2.2f USD, ", $baseval * $rateof{'U'});
  printf("%2.2f Franc, ", $baseval * $rateof{'F'});
  printf("%2.2f DM, ", $baseval * $rateof{'D'});
  printf("%2.2f Yen, and ", $baseval * $rateof{'Y'});
  printf("%2.2f Pound\n\nAmount: ", $baseval * $rateof{'P'});
}

sub breakdown {
   @line = split(" ", $_);

   $amnt = $line[0];
   if ($#line == 1) {
     $curr = $line[1];
     $curr =~ tr/a-z/A-Z/;         # uppercase
     $curr = substr($curr, 0, 1);  # first char only
   } else { $curr = "U"; }
   return ($amnt, $curr);
}

sub read_exchange_rate {
  open EXCHRATES, "<exchange.db" or die "$!\n";

  while ( <EXCHRATES> ) {
    chomp; split;
    $curr = $_[0];
    $val  = $_[1];
    $rateof{$curr} = $val;
  }
  close(EXCHRATES);
}

Now, I'm getting this in Strawberry Perl when I use Open With(Yes, I'm on Windows): No such file or directory But if I double-click it, it starts fine, but the session looks kind of like this:

Please enter the amount, appending the first letter of the name of
the currency that you're using (franc, yen, deutschmark, pound) -
the default value is US dollars.

Amount: 5 y
0.00 USD, 0.00 Franc, 0.00 DM, 0.00 Yen, and 0.00 Pound

Amount:

Something is obviously wrong. I already changed all instances of chop to chomp. What do I do now?

kirbyfan64sos
  • 10,377
  • 6
  • 54
  • 75
  • When does that code from date from? It's really nasty-looking Perl4-style code. Far better Perl tutorials exist. See http://perl-tutorial.org/. – Dave Cross Mar 07 '13 at 15:52
  • That isn't a tutorial. It is from SAMS Teach Yourself UNIX in 24 Hours. That code was written in 1999 for Perl 5. It was never meant to be a tutorial; it was just there as a example program. – kirbyfan64sos Mar 07 '13 at 19:55
  • If it's in a book called "Teach Yourself..." then, yes, it is a tutorial. Perl has moved on a lot in the fourteen years since that book was written. And it seems that the author wasn't particularly up to date even then as `chop()` went out of date in 1994. I really recommend that you throw that book away. You are unlikely to learn anything useful from it. – Dave Cross Mar 07 '13 at 20:17
  • IT WAS ON UNIX!!! That book is on UNIX, not Perl. That snippet was just an example intended to spike intrest in Perl. It has been very useful in making me more comfortable with the shell. Plus, the author's system probably just had an older vesion of Perl. – kirbyfan64sos Mar 07 '13 at 20:34
  • 1
    Just because its a book on unix doesn't mean that its Perl code isn't unusably out of date. – Joel Berger Mar 07 '13 at 23:22
  • I am aware that the Perl code is out of date, but I am _not_ planning on throwing it away. – kirbyfan64sos Mar 07 '13 at 23:37

1 Answers1

6

The reason your code doesn't work is that chop returns the character removed from the end of the string, not the string with the last character removed. You should also be using chomp which is identical except that it returns the last character only if it is a newline. That avoids problems when the last line of a file isn't terminated with a newline.

In addition you must always start a Perl program with use strict and use warnings, and declare all variables at their first point of use.

You should also avoid calling subroutines with the ampersand & on the name. That hasn't been correct practice since Perl 4 - about twenty years ago. Your calls should look like

read_exchange_rate();
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • so, `chop; ($amnt,$curr) = &breakdown($_);` – Jonas Berlin Mar 07 '13 at 15:37
  • With `use strict` in place you want `my ($amnt, $curr) = breakdown($_)` – Borodin Mar 07 '13 at 15:41
  • ...except that `breakdown` takes no parameters, so `my ($amnt, $curr) = breakdown()` is correct. – Borodin Mar 07 '13 at 17:04
  • "Learning Perl (6th ed.)" still recommends the use of the `&` always (until you've learned all the built-ins in Perl). So, other than being perhaps ugly and unneeded in most cases, does it actually hurt anything to use it? – Craig Treptow Mar 07 '13 at 19:19
  • So then that part should look like this: ($amnt,$curr) = &breakdown (chomp($_)); And if I add use strict, it'll look like this: my ($amnt,$curr) = &breakdown (chomp($_)); Quick question: What the heck does my do? – kirbyfan64sos Mar 07 '13 at 20:00
  • @CraigTreptow: I am not a fan of the Llama books, but I can understand that the advice you quote could help beginners to avoid writing subroutine with names that clash with built-in operators. Randal is no more an oracle than the rest of us, and he seems to me to lean more towards strict C-style programming than was intended at the dawn of the language. Perl is embraced by those of us who feel it is an instinctive way to write software, but there are always people that I call "bricklayers" that prefer a language with tight rules. They will always wish that Perl wasn't what it is. – Borodin Mar 07 '13 at 21:00
  • @kirbyfan64sos: `my` declares a variable. Without knowing your background it is hard to help further, but it is generally considered to be a [Good Idea](http://en.wikipedia.org/wiki/Declaration_%28computer_programming%29). Variables declared with `my` will be created at that point of declaration and will exist until they are no longer required. It helps to declare variables because statements in a program can be certain of which value they are modifying. – Borodin Mar 07 '13 at 21:06
  • @borodin: Ok. Was the code I showed in my previous post correct? Oh, and what _did_ you mean when you said "Without knowing your backround"? – kirbyfan64sos Mar 07 '13 at 21:11
  • @kirbyfan64sos: I apologise for being unclear. What you wrote will work, but it is rare to see the ampersand sigil on subroutine calls in modern Perl. Calling code this way also does something very specific with the `@_` parameter array that may be useful in core modules but is irrelevant to ordinary programs. In general you shouldn't use it. Because the use of the ampersand is related to low-level concepts, I didn't know how to express the ideas without knowing what languages and disciplines you were familiar with, and so what terms I could use while remaining understood. – Borodin Mar 07 '13 at 22:23
  • @borodin: Got that. I come from a Python world, so a lot of this is new to me. One more question: can you tell me what the heck is wrong with the updated code(I updated my main post)? – kirbyfan64sos Mar 07 '13 at 23:35
  • @kirbyfan64sos: The same as before. `chomp` returns the character removed. Use `chomp; my ($amnt, $curr) = breakdown()`. – Borodin Mar 08 '13 at 06:35
  • @borodin: Still doesn't work. It's giving me 0 dollars in every currency. Can't figure it out. – kirbyfan64sos Mar 09 '13 at 00:02
  • Have you added `use strict` and `use warnings` as I advised? I can't see what the problem is unless I can see your complete code, perhaps on [pastebin](http://pastebin.com)? – Borodin Mar 09 '13 at 00:30
  • @borodin: Here is the new code: http://pastebin.com/VsqyGDF4 The error: http://pastebin.com/nxmZxEg3 Can you upload a working version to the same site? – kirbyfan64sos Mar 10 '13 at 21:21