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