[comp.lang.perl] variable is numeric?

pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) (03/16/90)

What is the Canonical Test of whether a variable is numeric?


Paul O'Neill                 pvo@oce.orst.edu
Coastal Imaging Lab
OSU--Oceanography
Corvallis, OR  97331         503-737-3251

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/17/90)

In article <16918@orstcs.CS.ORST.EDU> pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) writes:
: What is the Canonical Test of whether a variable is numeric?

I'm not sure what you mean by that.  All scalar variables are potentially
numeric, and any scalar that has been evaluated in a numeric context has
a numeric value.  That value will be whatever atof() returned.  So non-numeric
strings simply have the numeric value 0, along with their string value (a
variable actually can have a string value and a numeric value simultaneously--
hopefully these always stay in sync, but a recent patch fixed substr() because
it allowed the values to get out of sync (one variable is purposefully
forced to be out of sync, and that's $!)).

There is no way that I know of (apart from the magical behavior of
autoincrement) to tell whether a variable has been referenced in a numeric
context.

If you're asking about how Perl decides whether to use $# in printing out
a number, it does it if it already has a non-zero numeric value, or if
the string part matches /^\s*[+-]?\d*(\.?\d*([eE][+-]?\d*)?)?\s*$/ and
atof() says it has a non-zero value.  (Actually, that regexp is a translation
of the C code of looks_like_number(), which is a heap more efficient than
that regexp would be.)

Larry

pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) (03/17/90)

In article <7443@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:
>In article <16918@orstcs.CS.ORST.EDU> pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) writes:
>: What is the Canonical Test of whether a variable is numeric?
>
>I'm not sure what you mean by that.

Hmm.  Looks like I tried to save too much bandwidth. :-)

A lot of survey data comes through this lab.  Data from different institutions
using different instruments in different formats.  Some is in feet that needs
converting to meters.

Almost invariably, the data needing conversion appear as a triplet of
numbers.  What a great job for perl!

My perl script to convert surveyed feet to surveyed meters justs looks for
a line w/ 3 consecutive numbers and multiplies all such triplets by
0.3048006.  

BUT IT'S UGLY:

-----------------------------------------------------------------------
#!/usr/local/bin/perl
#
# ft2m_any.perl
# 27 feb 90
# Paul V. O'Neill -- Coastal Imaging Lab, OSU, Corvallis, OR 97331
#
# feet to meters convertion on lines consisting of 3 consecutive numbers
# leave other lines alone
#
format STDOUT = 
        @<<<<<<         @<<<<<<         @<<<<<<<
        $meters[0],     $meters[1],     $meters[2]
.

while (<>) {                            # get a line at a time
    $save = $_;
    @feet = /([0123456789.]*)\s*([0123456789.]*)\s*([0123456789.]*)\s*/;
    if($feet[2]) {
        @meters = grep($_ *= .304800609601, @feet );
        write;
    } else {
        print $save;
    }
}
--------------------------------------------------------------

[0123456789.]  seems a gross way to identify a number.  And "10.33.9" would
pass the test.

If perl knows that a variable is numeric (automagical increment writeup)
why can't I know?  I'd much prefer to split the current line and do some
cute, canonical test on each member of @_ for "numberness."

Thanks.




Paul O'Neill                 pvo@oce.orst.edu
Coastal Imaging Lab
OSU--Oceanography
Corvallis, OR  97331         503-737-3251

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/17/90)

In article <16943@orstcs.CS.ORST.EDU> pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) writes:
: A lot of survey data comes through this lab.  Data from different institutions
: using different instruments in different formats.  Some is in feet that needs
: converting to meters.
: 
: Almost invariably, the data needing conversion appear as a triplet of
: numbers.  What a great job for perl!
: 
: My perl script to convert surveyed feet to surveyed meters justs looks for
: a line w/ 3 consecutive numbers and multiplies all such triplets by
: 0.3048006.  
: 
: BUT IT'S UGLY:
: 
: -----------------------------------------------------------------------
: #!/usr/local/bin/perl
: #
: # ft2m_any.perl
: # 27 feb 90
: # Paul V. O'Neill -- Coastal Imaging Lab, OSU, Corvallis, OR 97331
: #
: # feet to meters convertion on lines consisting of 3 consecutive numbers
: # leave other lines alone
: #
: format STDOUT = 
:         @<<<<<<         @<<<<<<         @<<<<<<<
:         $meters[0],     $meters[1],     $meters[2]
: .
: 
: while (<>) {                            # get a line at a time
:     $save = $_;
:     @feet = /([0123456789.]*)\s*([0123456789.]*)\s*([0123456789.]*)\s*/;
:     if($feet[2]) {
:         @meters = grep($_ *= .304800609601, @feet );
:         write;
:     } else {
:         print $save;
:     }
: }
: --------------------------------------------------------------
: 
: [0123456789.]  seems a gross way to identify a number.  And "10.33.9" would
: pass the test.

You can say [0-9.].  Or [\d.]. 

If you're worried about multiple dots, I'd suggest \d*\.?\d+ or \d+\.?\d*.
Do you allow ".1" or only "0.1"?

: If perl knows that a variable is numeric (automagical increment writeup)
: why can't I know?  I'd much prefer to split the current line and do some
: cute, canonical test on each member of @_ for "numberness."

What's the matter with (assuming no leading .'s)

	@feet = grep(/^\d+\.?\d*$/,split(' '));
	if (@feet == 3) {		# found 3 numbers (patchlevel 12)

Or maybe, if your numbers are always greater than 0

	@feet = grep($_ + 0, split(' '));

Of course, if that's true, there's always

	if ($feet[0] * $feet[1] * $feet[2]) {

If they can be zero, how about

	@feet = grep($_ + 0 || /^0\.?0*$/, split(' '));

or

	@feet = grep($_ + 0 || /^0?\.?0+$/, split(' '));

Presuming your triples are on a line by themselves, and you don't care
much about how much whitespace or precision you end up with, you could
probably write your script as

	while (<>) {
	    @nums = grep((s/\.//,/^-?\d+$/), split(' '));
	    s/([\d.]+)/$1 * .304800609601/eg if @nums == 3;
	    print;
	}

I would try to avoid the write in any case:

        while (<>) {
            @meters = grep(/^0\.?0*$/ || $_ *= .304800609601, split(' '));
            $_ = sprintf("        %7.7s         %7.7s         %8.8s\n",@meters)
                if @meters == 3;
            print;
        }

This can be reduced to

	#!/usr/local/bin/perl -p
	@meters = grep(/^0\.?0*$/ || $_ *= .304800609601, split(' '));
	$_ = sprintf("        %7.7s         %7.7s         %8.8s\n",@meters)
	    if @meters == 3;

In general, perl never knows whether your string looks like a number,
because it just assumes it's a number and forces the issue whenever you
treat it like a number.  It would be wasting CPU to do otherwise.
Seeing that your variable != 0 is at least indicative that the front
of the string looked like a non-zero number.  Other than than, you have
to be explicit.

Larry