Elegant ways to convert '' or 'number' to a number

T

Tim McDaniel

So a cow-orker has the result of a database query. It's being
returned as a string: it contains an integer or it's a null string.
(Yes, we're certain.) He wants to use it in a numeric context, but is
getting
Argument "" isn't numeric in numeric eq (==) at FILE line NUMBER.
warnings (as we have "use warnings" on). I had had the impression
that "+ 0" would do the conversion and avoid a warning, but that's not
the case.

Is there an elegant idiom for converting such a string to a number
without producing a warning if it happens to be a null string?

* $t = $t ? 0+$t : 0;
is not what I would call "elegant", especially in this case, where
it's not $t but $alonghashtablename{AVERYLONGHASHINDEXNAME}.

* $t = "0$t" + 0;
isn't so elegant either (though at least it does not convert to
octal, as I first wondered).

* $t ||= 0;
doesn't give you a number per se, but at least the string converts
to a number in numeric contexts without a warning.

* ($t ||= 0) += 0;
Its main virtue is that it converts it to a number and only uses $t
once.

I'm thinking that
{ no warnings 'numeric'; $t += 0; }
is the best choice. It has the great virtue of being abundantly clear
on what they want, as opposed to a person reading having to figure out
some hack that happens to do it without a warning.

I should note that we're using 5.8.8 and NO I have no way whatsoever
of changing that.
 
J

J. Gleixner

So a cow-orker has the result of a database query.


I didn't know cows used databases.. :)
It's being
returned as a string: it contains an integer or it's a null string.
(Yes, we're certain.) He wants to use it in a numeric context, but is
getting
Argument "" isn't numeric in numeric eq (==) at FILE line NUMBER.
warnings (as we have "use warnings" on). I had had the impression
that "+ 0" would do the conversion and avoid a warning, but that's not
the case.

If you need to have warnings enabled, for some reason, then it might
be cleaner to fix the source of the problem. e.g. the SQL ...
ISNULL or NVL or IFNULL or ....

I generally don't 'use warnings' in production, because of these and the
uninitialized warnings. Just lazy, I guess.

Another solution, don't try to modify the value, just shortcut it and
only let it try the numeric test, it if it's defined:

if( defined $val && $val == 1234 )

That won't help when printing $val though..
 
T

Tim McDaniel

Quoth (e-mail address removed):

By 'null string' you mean an empty string, not undef, right?

Yes. I agree with Cerebron on dragons: the concepts ought to be kept
rigidly distinct.
 
R

Rainer Weikusat

Yes. I agree with Cerebron on dragons: the concepts ought to be kept
rigidly distinct.

They are not different concepts and Perl and have never been different
concepts in Perl: Perl is one of those 'messy' languages designed
around the idea that automatic conversions are a good thing because
they reduce the amount of boilerplate code people need to write (and
other people need to read). Other languages have been designed in
different ways and might be more suitable who consider those different
ways essential.
 
T

Tim McDaniel

They are not different concepts and Perl and have never been
different concepts in Perl

They can be distinguished via defined(). They cause different
warnings (uninitialized value).

More fundamentally, I do consider it worthwhile to distinguish between
different values of missing data.
 
R

Rainer Weikusat

They can be distinguished via defined(). They cause different
warnings (uninitialized value).

They are not disinguishable for their 'string value' because automatic
conversions are done by Perl as required, as I already wrote. Provided
run-time warnings are enabled, some pretty random looking subset of
the situations where such an automatic conversion takes places cause
some text to be printed. That's an optional feature some people
consider to be useful (primarily for others, as it seems ...).
More fundamentally, I do consider it worthwhile to distinguish between
different values of missing data.

http://en.wikipedia.org/wiki/Law_of_excluded_middle
 
T

Tim McDaniel

They are not disinguishable for their 'string value' because
automatic conversions are done by Perl as required

Nevertheless, they are different concepts, although there is only one
place (defined()) where code can tell the difference, so far as I
know.
 
C

ccc31807

So a cow-orker has the result of a database query.  It's being
returned as a string: it contains an integer or it's a null string.
(Yes, we're certain.)  He wants to use it in a numeric context,
Is there an elegant idiom for converting such a string to a number
without producing a warning if it happens to be a null string?

What's wrong with using int(), or sprintf()?

I had a similar problem, but the reverse. I used person ID numbers as
keys in a hash table, and manipulated the values in various ways,
which included Microsoft Excel. I kept getting aggravating errors over
a long time, and after one experience went through the results line by
line and discovered that sometimes ID numbers with leading zeros were
treated like real numbers, and a value like '4321' does not match a
hash key like '0004321'.

I started using sprintf() when in doubt, and it solved the problem. I
just convert whatever the value is into a string, and it preserves the
leading zeros.

If you want to reverse the process, int() would probably work and be
less verbose than sprint().

CC.

CC.
 
G

Graham Drabble

So a cow-orker has the result of a database query. It's being
returned as a string: it contains an integer or it's a null
string.

Has he though of changing the query. You don't say what DB he's using
but the following should work in MSSQL and I would expect similar to
be possible in other DBMSs.

Instead of
"SELECT
column
from table"

use

"SELECT
case
when column is null then 0
else column
end
from table"
 
T

Tim McDaniel

What's wrong with using int(),

The part about "without producing a warning if it happens to be a null string".

$ perl -e 'use warnings; int("")'
Argument "" isn't numeric in int at -e line 1.

Since it does happen to be an integer or the null string, I think "0+"
is equivalent to int().
or sprintf()?

Not sure what you mean in this case. Do you mean this?

$ perl -e 'use warnings; my $x = sprintf("%d", ""); print "[$x]\n"'
Argument "" isn't numeric in sprintf at -e line 1.
[0]
 
R

Rainer Weikusat

[...]
They are not disinguishable for their 'string value' because
automatic conversions are done by Perl as required

Nevertheless, they are different concepts, although there is only one
place (defined()) where code can tell the difference, so far as I
know.

They are 'something different' at the perl implementation level. If
they are also 'something different' for a particular application of
Perl to some problem depends on the problem: Values usually have types
and such a type is the set of all valid values for a particular
'thing'. If this set includes some kind of 'null value', something
which is technically legitimate but no operations save than comparing
it to other valid values may be used on it and the result will always
be "it's differen", Perl default-value scalars can be used to
represent this 'null value'. An example of that would be DBI which
represents the SQL concept of 'the null value' in this way. OTOH, if
I'm dealing with the values from the mod 256 factor ring, treating the
same default value scalar as a variable which is automatically
initialized to a value of 0 will often be more convenient, no matter
how thoroughly the very idea combs zealous beancounters against the
grain (I have no idea if this works in English, just some web-research
based hopes that it does).
 
C

ccc31807

The part about "without producing a warning if it happens to be a null string".

Sorry, I guess I didn't notice that.

Here is a common idiom that I employ a lot:

$x ||= 0;

Obviously, this only applies when $x does not contain any value.

When validating data, for example, ensuring that a field contains
something similar to an email address, I do this a lot:

$x ||- '(e-mail address removed)';

or

$x = '(e-mail address removed)' unless $x =~ /\w+\@.\w+/;

Finally, if you automate your scripts, perhaps by using a cron job or
a scheduled task, no one sees the warnings. In many (most?) of my
production scripts, I don't disable warnings. My users never see the
warnings, but only the output -- however, when I run the scripts in
real time, I see the warnings, and somehow that reassures me that
everything's right with the world.

CC.
 
R

Rainer Weikusat

[...]

Finally, if you automate your scripts, perhaps by using a cron job or
a scheduled task, no one sees the warnings.

FYI: Usually, cron will mail the stdout and stderr output of a cronjob to
the user whose cronjob it was.
 
T

Tim McDaniel

If they are also 'something different' for a particular application
of Perl to some problem depends on the problem

Hm. I think I agree with your point. Perhaps I might put it that,
from the point of view of "duck typing", what matter is what you want
to do with it and what the language conveniently allows.

I'm not sure whether it's my FORTRAN and C background, or my general
anal-retentiveness, that makes me focus too much on types and to look
with suspicion on Perl's builtin conversions, instead of relaxing and
letting Perl deal with it -- in the case I was asking about, letting
the "==" operator handle the conversion.
no matter how thoroughly the very idea combs zealous beancounters
against the grain (I have no idea if this works in English, just some
web-research based hopes that it does).

"Beancounter", as I've heard it, refers specifically to accountants,
and that word doesn't have connotations that work here. I'd say
"no matter how thoroughly the very idea rubs pedants the wrong way".
 
P

Peter J. Holzer

FYI: Usually, cron will mail the stdout and stderr output of a cronjob to
the user whose cronjob it was.

Right. And getting a mail with a warning every 10 minutes should be
enough enough motivation to fix the problem fast (of course some people
would rather implement a mail filter to automatically delete all mails
from cron ...)

hp
 
P

Peter J. Holzer

Has he though of changing the query. You don't say what DB he's using
but the following should work in MSSQL and I would expect similar to
be possible in other DBMSs.

Instead of
"SELECT
column
from table"

use

"SELECT
case
when column is null then 0
else column
end
from table"

Tim already wrote (unless I misunderstood him), that the value is
actually "", not undef. DBI always returns undef for NULL, so that
wouldn't help. (This also means that the column has almost certainly a
varchar type, not a number type, which hints at a deeper database design
problem - but unfortunately we often have to live with databases as they
are and can't fix them).

Somethingh like

SELECT
case column
when '' then 0
else column
end
from table

should work, though.

hp
 
P

Peter J. Holzer

Nonsense; interpolating an empty string does not produce a warning.
There are certainly cases where you can ignore the difference, but the
difference is nonetheless real.

More importantly: If Larry had intended undef to to conceptually the
same as an empty string he wouldn't have bothered to implement undef
at all. Why implement a special value with a special keyword if it's
just the same as an empty string? So Larry considered it important to be
able to distinguish between undef (no value, missing value, unknown
value, ...) and the empty string or 0, even if he provided for automatic
conversion.

hp
 
P

Peter J. Holzer

What's wrong with using int(), or sprintf()?

I had a similar problem, but the reverse. I used person ID numbers as
keys in a hash table, and manipulated the values in various ways,
which included Microsoft Excel. I kept getting aggravating errors over
a long time, and after one experience went through the results line by
line and discovered that sometimes ID numbers with leading zeros were
treated like real numbers, and a value like '4321' does not match a
hash key like '0004321'.

Well, you have to know your data: Perl itself won't convert a string
'0004321' into the number 4321 unless you force it to. Contrary to what
Rainer probably thinks numbers and strings aren't the same in Perl and
if you keep a mental model of what your values actually are it isn't
actually that hard to prevent accidental type conversions.

I started using sprintf() when in doubt, and it solved the problem. I
just convert whatever the value is into a string, and it preserves the
leading zeros.

You haven't shown the sprintf invokation you use but I don't think so.
It may *add* leading zeros if they got lost, but it cannot preserve
them.

hp
 
T

Tim McDaniel

Tim already wrote (unless I misunderstood him), that the value is
actually "", not undef. DBI always returns undef for NULL, so that
wouldn't help. (This also means that the column has almost certainly
a varchar type, not a number type, which hints at a deeper database
design problem - but unfortunately we often have to live with
databases as they are and can't fix them).

In particular, this is Oracle. As I understand it, it implements
varchar NULL as a null string. It may be DBI, or the database
wrappers we've put around it, or other functions around them, but my
cow-orker reported that it was indeed a null string when he got it.
 
R

Rainer Weikusat

[...]
Contrary to what Rainer probably thinks numbers and strings aren't
the same in Perl

Assumptions you make about other people's thoughts are YOUR thoughts.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top