Scalars: Building a Currency Converter

In this conclusion to a five-part series on scalars in Perl, we’ll put everything we’ve learned together to build a currency converter. This article is excerpted from chapter two of the book Beginning Perl, written by James Lee (Apress; ISBN: 159059391X).

Multiple Assignments

We’ve said that = is an operator, but does that mean it returns a value? Well, actually it does, it returns whatever was assigned. This allows us to set several variables up at once. Here’s a simple example of this; read it from right to left:

$d = $c = $b = $a = 1;

First we set $a to 1, and the result of this is 1. $b is set with that, the result of which is 1. And so it goes on.

Scoping

All the variables we’ve seen so far in our programs have been global variables. That is, they can be seen and changed from anywhere in the program. For the moment, that’s not too much of a problem, since our programs are very small, and we can easily understand where things get assigned and used. However, when we start writing larger programs, this becomes a problem.

Why is this? Well, suppose one part of your program uses a variable, $counter . If another part of your program wants a counter, it can’t call it $counter as well for fear of clobbering the old value. This becomes more of an issue when we get into subroutines, which are little sections of code we can temporarily call upon to accomplish something for us before returning to what we were previously doing. Currently, we’d have to make sure all the variables in our program had different names, and with a large program that’s not desirable. It would be easier to restrict the life of a variable to a certain area of the program.

To achieve this, Perl provides another type of variable, called lexical variables. These are constrained to the enclosing block and all blocks inside it. If they’re not currently inside a block, they are constrained to the current file. To tell Perl that a variable is lexical, we say my $variable; . This creates a brand-new lexical variable for the current block, and sets it to the undefined value. Here’s an example:

#!/usr/bin/perl -w
# scope1.pl

$record = 4;
print "We’re at record ", $record, "n";
{
   my $record;
   $record = 7;
   print "Inside the block, we’re at record ", $record, "n";
}

print "Outside, we’re still at record ", $record, "n";

And this should tell you

$ perl scope1.pl
We’re at record 4
Inside the block, we’re at record 7
Outside we’re still at record 4
$

Let’s look at how this program works. Firstly, we set our global variable $record to 4.

$record = 4;
print "We’re at record ", $record, "n";

Now we enter a new block, and create a new lexical variable. Important! This is completely and utterly unrelated to the global variable $record as my() creates a new lexical variable. This exists for the duration of the block only, and has the undefined value.

{
   my $record;

Next, the lexical variable is set to 7 , and printed out. The global $record is unchanged.

   $record = 7;
   print "Inside the block, we’re at record ", $record, "n";

Finally, the block ends, and the lexical copy ends with it. We say that it has gone out of scope. The global remains however, and so $record has the value 4.

}

print "Outside, we’re still at record ", $record, "n";

In order to make us think clearly about our programming, we will ask Perl to be strict about our variable use. The statement use strict; checks that, amongst other things, we’ve declared all our variables. We declare lexicals with the my() function. Here’s what happens if we change our program to use strict format:

#!/usr/bin/perl -w
# scope2.pl
use strict;

$record = 4;
print "We’re at record ", $record, "n";

{
   my $record;
   $record = 7;
   print "Inside the block, we’re at record ", $record, "n";
}

print "Outside, we’re still at record ", $record, "n";

Now, the global $record is not declared. So sure enough, Perl complains about it, gener ating this output:

$ perl scope2.pl
Global symbol "$record" requires explicit package name at scope2.pl line 6.
Global symbol "$record" requires explicit package name at scope2.pl line 7.
Global symbol "$record" requires explicit package name at scope2.pl line 15.
Execution of scope2.pl aborted due to compilation errors.
$

We’ll see exactly what this means in later chapters, but for now it suffices to declare $record as a my() variable:

#!/usr/bin/perl -w
# scope3.pl

use strict;

my $record;
$record = 4;
print "We’re at record ", $record, "n";

{
   my $record;
   $record = 7;
   print "Inside the block, we’re at record ", $record, "n";
}

print "Outside, we’re still at record ", $record, "n";

Now Perl is happy, and we get the same output as before. You should almost always start your programs with a use strict . Of course, nobody’s going to force you, but it will help you avoid a lot of mistakes, and will certainly give other people who have to look at your code more confidence in it.

{mospagebreak title=Variable Names}

We’ve not really examined yet what the rules are regarding what we can call our variables. We know that scalar variables have to start with a dollar sign, but what next? The next character must be a letter (uppercase or lowercase) or an underscore, and after that, any combination of numbers, letters, and underscores is permissible.

Note that Perl’s variable names, like the rest of Perl, are case-sensitive, so $user is different from $User , and both are different from $USER .

The following are legal variable names: $I_am_a_long_variable_name , $simple , $box56 , $__hidden , $B1 .

The following are not legal variable names: $10c (doesn’t start with letter or underscore), $mail-alias (- is not allowed), $your name (spaces not allowed).

The Special Variable $_

There are certain variables, called special variables, which Perl provides internally that you either are not allowed to or do not want to overwrite. One which is allowed by the preceding rules is $_ , a very special variable indeed. $_ is the default variable that a lot of functions read from, write to, and operate upon if no other variable is given. We’ll see plenty of examples of it throughout the book. For a complete list of all the special variables that Perl uses and what they do, type perldoc perlvar at the command line.

Variable Interpolation

We said earlier that double-quoted strings interpolate variables. What does this mean? Well, if you mention a variable, say $name, in the middle of a double-quoted string, you get the value of the variable, rather than the actual characters. As an example, see what Perl does to this:

#!/usr/bin/perl -w
# varint1.pl
use strict;

my $name = "fred";
print "My name is $namen";

This produces

$ perl varint1.pl
My name is fred
$

Perl interpolates the value of $name into the string. Note that this doesn’t happen with single-quoted strings, just like escape sequence interpolation:

#!/usr/bin/perl -w
# varint2.pl

use strict;
my $name = "fred";
print ‘My name is $namen’;

Here we get

$ perl varint2.pl
My name is $namen$

Notice that the system prompt is printed at the end of that line because n is not a newline character within the single quotes. This doesn’t just happen in things we print, it happens every time we construct a string:

#!/usr/bin/perl -w
# varint3.pl

use strict;

my $name = "fred";
my $salutation = "Dear $name,";
print $salutation, "n";

This gives us

$ perl varint3.pl
Dear fred,
$

This has exactly the same effect as

my $salutation = "Dear " . $name . ",";

but is more concise and easier to understand.

If you need to place text immediately after the variable, you can use curly braces to delimit the name of the variable. Take this example:

#!/usr/bin/perl -w
# varint4.pl

use strict;

my $times = 8;
print "This is the $timesth time.n";

This is syntactically incorrect, because Perl looks for a variable $timesth which hasn’t been declared. In this case, we have to change the last line by wrapping the variable name in curly braces to this:

print "This is the ${times}th time.n";

Now we get the right result:

$ perl varint4.pl
This is the 8th time.
$

{mospagebreak title=Currency Converter}

Let’s begin to wind up this chapter with a real example—a program to convert between currencies. This is our very first version, so we won’t make it do anything too clever. As we get more and more advanced, we’ll be able to hone and refine it.

#!/usr/bin/perl -w
# currency1.pl

use strict;

my $yen = 105.6;  # as of 02 February 2004 print "49518 Yen is ", (49_518/$yen), " dollarsn";
print "360 Yen is   ", (   360/$yen), " dollarsn";
print "30510 Yen is ", (30_510/$yen), " dollarsn";

Save this, and run it through Perl. You should see something like this:

$ perl currency1.pl
49518 Yen is 468.920454545455 dollars
360 Yen is   3.40909090909091 dollars
30510 Yen is 288.920454545455 dollars
$

First, we declare the exchange rate to be a lexical variable and set it to 105.6 .

my $yen = 105.6;

Notice that we can declare and assign a variable at the same time. Now we do some calcu lations based on that exchange rate:

print "49518 Yen is ", (49_518/$yen), " dollarsn";
print "360 Yen is   ", (   360/$yen), " dollarsn";
print "30510 Yen is ", (30_510/$yen), " dollarsn";

Of course, this is currently of limited use, because the exchange rate changes, and we might want to change some different amounts at times. To do either of these things, we need to be able to ask the user for additional data when we run the program.

Introducing <STDIN>

Perl reads from standard input (the keyboard) with <STDIN> . It reads up to and including the newline character, so the newline is part of the string read in. To read a single line of input from the user we can say something like

print "Please enter something interestingn";
$comment = <STDIN>;

This will read one line from the user, including the newline character, and assign the string that was read to the variable $comment . Let’s use this to get the exchange rate from the user when the program is run. This example will read the exchange rate from the user’s keyboard, storing it in $yen :

#!/usr/bin/perl -w
# currency2.pl

use strict;

print "Currency converternnPlease enter the exchange rate: ";
my $yen = <STDIN>;

print "49518 Yen is ", (49_518/$yen), " dollarsn";
print "360 Yen is   ", (   360/$yen), " dollarsn";
print "30510 Yen is ", (30_510/$yen), " dollarsn";

Now when you run the program, you’ll be asked for the exchange rate. The currency values will be calculated using the rate you entered:

$ perl currency2.pl
Currency converter

Please enter the exchange rate: 100
49518 Yen is 495.18 dollars
360 Yen is   3.6 dollars
30510 Yen is 305.1 dollars
$

Note that this time we read the exchange rate from the user’s keyboard and it was read in as a string. Perl converts the string to a number in order to perform the calculation.

So far, we haven’t done any checking to make sure that the exchange rate given makes sense; this is something we’ll need to think about in the future.

{mospagebreak title=The chomp() and chop() Functions}

<STDIN> reads up to and including the newline character. Sometimes we don’t want to include the newline in the text we have read, so we can chomp() the newline off the string.

The chomp() function removes the last character of a string if and only if it is the newline character. For instance:

$string = "testing 1, 2, 3n" ;
chomp($string);                # $string is now "testing 1, 2, 3"

Since <STDIN> reads up to and including the newline character, this code reads and then removes the newline:

my $input = <STDIN>;
chomp($input);

Those two statements can be combined into one:

chomp(my $input = <STDIN>);

A related function is chop() . It removes the last character of a string, regardless of what character it is. Here is an example:

$string = "testing, 1, 2, 3";
chop($string);                 # $string is now "testing 1, 2, "

Two Miscellaneous Functions

Before we end our discussion of scalars, we should discuss two functions that are often used to terminate Perl programs: exit() and die().

The exit() Function

The exit() function exits the program. If an argument is provided, it returns that value back to the calling program (or shell). If no argument is provided, it returns back the value 0. In the shell, the value 0 means that the program terminated normally, so we can report that all is well with

exit(0);

or

exit;

If the program exits abnormally due to some error condition, simply return back a nonzero value to tell the calling program that all is not well:

exit(1);

Here is an example of using the exit() function:

#!/usr/bin/perl -w
# exit.pl

use strict;

print "enter value to return back to the calling program: ";
chomp(my $value = <STDIN>);

exit($value);

In Unix, we can echo out the value $? to see the return value of the most recent command:

$ perl exit.pl
enter value to return back to the calling program: 0
$ echo $?
0
$ perl exit.pl
enter value to return back to the calling program: 255
$ echo $?
255
$

{mospagebreak title=The die() Function}

The die() function is how we handle severe errors in Perl. It takes a character string argument and prints it to standard error output (this normally prints to the screen like standard output does). If the argument string does not end in newline, the n character, die() automatically appends to the output string the name of the Perl program and the line number of the program where the die() was executed; this is very helpful—it tells us right where the error took place. Then die() cleans up the program and exits with a non-0 exit status. Therefore, die() is a permanent solution—the program terminates:

die "there was an error";

Here is an example of using die() :

#!/usr/bin/perl -w
# die.pl

use strict;

print "please enter a string to pass to die: ";
chomp(my $string = <STDIN>);

die($string);
print "didn’t make it this far…n";

Executing this code would resemble

$ perl die.pl
please enter a string to pass to die: this is the end
this is the end at die.pl line 9, <STDIN> line 1.
$

Notice that the name of the script and the line number is automatically added to the output of die() because the argument to die() did not end in the newline character. Also notice that the last print() is not executed because the program terminated when die() executed.

Summary

Perl’s basic data type is a scalar. A scalar can be either an integer, floating point number, or string. Perl converts between these three automatically when necessary.

Double- and single-quoted strings differ in the way they process the text inside them. Single-quoted strings do little to no processing at all, whereas double-quoted strings inter polate escape sequences and variables.

We can operate on these scalars in a number of ways—ordinary arithmetic, bitwise arithmetic, string manipulation, and logical comparison. We can also combine logical comparisons with Boolean operators. These operators vary in precedence, which is to say that some take effect before others, and as a result we must use parentheses to enforce the precedence we want.

Scalar variables are a way of storing scalars so that we can get at them and change their values. Scalar variable names begin with a dollar sign ( $ ) and are followed by one or more alphanumeric characters or underscores. There are two types of variables—lexical and global. Globals exist all the way through the program, and so can be troublesome if we don’t keep very good track of where they are being used. Lexicals have a life span of the current block, and so we can use them safely without worrying about clobbering similarly named variables somewhere else in the program.

<STDIN> reads in from standard input, which is normally the user’s keyboard. We can store this input in a variable and then operate upon it, making our programs more flexible. <STDIN> reads up to and including the newline character, and we normally chomp() off the newline.

Two ways to terminate our programs are by using exit() and die() . die() is helpful because it prints its argument, and if that argument does not end in n , it magically adds the script name and line number to the output which helps us locate the error.

Exercises

  1. Change the currency conversion program so that it asks for an exchange rate and three prices to convert.
  2. Write a program that asks for a hexadecimal number and converts it to decimal. Then change it to convert an octal number to decimal. 
     
  3. Write a program that asks for a decimal number less than 256 and converts it to binary. (Hint: You may want to use the bitwise and operator 8 times.) 
     
  4. Without the aid of the computer, work out the order in which each of the following expressions would be computed, and their value. Put the appropriate parentheses in to reflect the normal precedence: 
     
    • 2+6/4-3*5+1 
       
    • 17+-3**3/2 
       
    • 26+3^4*2 
       
    • 4+3>=7||2&4*2<4 
     
     
       

 

 

 

 

Google+ Comments

Google+ Comments