4.5.2 Checking for Scalar Types: Using Both the Caret and $ Anchors

4.5.2  Checking for Scalar Types: Using Both the Caret and $ Anchors

 Perl does not provide any built-in type checking for its variables and constants. Suppose we have a scalar variable and want to determine what kind of a variable it is. The following program is a first attempt at such type checking for scalar variables.

 Program 4.16

#!/usr/bin/perl
use strict;

sub findScalarType{
    my ($item) = @_;
  
    if ($item =~ /^\d+$/) 
        {print "whole number\n";}
    elsif ($item =~ /^[-+]?\d+$/) 
        {print "signed integer\n";}
    elsif ($item =~ /^[-+]?\d+(\.\d*)?$/)
       {print "non-integral decimal number\n";} #case 1
    elsif ($item =~ /^[-+]?\.\d+$/)
       {print "non-integral decimal number\n";} #case 2
    elsif ($item =~ /^\D+$/)
       {print "string\n";}
    elsif ($item =~ /\D/) 
       {print "possibly string\n";}
} 

Subroutines in Perl start with the keyword sub. It is followed by the name of the subroutine. The body of the subroutine is enclosed inside braces. The subroutine findScalarType takes one argument that is a scalar. In Perl, inside a subroutine, all its arguments are available in terms of a special array variable called @_. Here, we have only one argument to the subroutine. We extract this argument from @_ and call it
$item
. The subroutine, simplistic as it is, prints one message for an input given to it.

The first if statement contains the pattern expression

 

$item =~ /^\d+$/

 

as the conditional. The pattern is simply \d+, but it is anchored at the front of the target string $item as well as at the end. This essentially means that the whole string must be covered from the beginning to the end by the pattern. In other words, the string or scalar we are looking at must have only digits in it. That is, it a whole number or an unsigned integer.

The next if statement contains the pattern for a signed integer, one that has a + or - in the front.

The next two if statements look for decimal numbers (i.e., numbers with base 10) that are not yet covered. In other words, they look for decimal numbers that are not integers. The first one of these has the following conditional.

 

$item =~ /^[-+]?\d+(\.\d*)?$/

 

It looks for an optional sign followed by one or more digits. After this first set of digits, there may be a decimal number followed by zero or more digits. This first alternative matches a string or a scalar such as +123, -123.45, 123, "123.0", or "123.45". The second alternative matches strings like "+0.56" or "-0.56". Note that this may not be the best definition of decimal numbers, but it is a good start.

Here are some calls to the subroutine. The calls

 

findScalarType ("abc");

findScalarType ("123");

findScalarType (123);

 

print string, whole number and whole number respectively.

Each of the calls

 

findScalarType (123.45);

findScalarType (-0.25);

findScalarType (123e-5);

findScalarType (-.25);

 

prints non-integral decimal number (case 1). The first two are obvious. However, the third and the fourth cases are not so obvious. In the third case, Perl converts 123e-5 to 0.00123 first before sending it to the function. As a result it falls under the first case of non-integral decimal numbers. In the fourth case, Perl converts -.25 to -0.25 before sending it to the function. Therefore, even in this case, the first alternative for non-integral decimal numbers holds.

The call

 

findScalarType (".25");

 

prints the second alternative for non-integral decimal number. It is because Perl considers the argument to be a string and does not do any numeric conversion of any kind before sending it to the function.

Finally, the following two calls

 

findScalarType ("abc2d");

findScalarType ("123e-5");

 

print possibly string.