Re: When warnings get in the way
by halley (Prior) on Apr 22, 2005 at 16:59 UTC
|
This isn't in direct answer to your question, but may help you to code in a "more Perlish" way, and have to work less to get the answers you want.
The traditional value for "falsehood" in Perl is undef. When converted to a string, that looks like "", and when converted to a number, that looks like 0, and when converted to a list, that looks like (); all three are also considered false. The special string "0" is also considered false, because it would convert to a false value if treated as a number. These are the only values considered false, by the way.
The traditional value for "truth" in Perl is 1. Actually, anything that is not false is considered true, so your string "true" is suitable, but perhaps overkill and misleading too.
Coding to expect truth or false by comparing against a specific value may mislead your readers. Use specific values when they're important, but use 1/undef when you just want to keep a boolean flag value. Code your conditionals to just look at that flag, and not compare that flag to some specific value. Getting in that habit will help you avoid introducing errors when the variable is true but not equal to "true" for example.
Consider coding this way:
$foo = 1;
if ($foo) { print "Yes, it's true.\n"; }
if (not $foo) { print "No, it's false.\n"; }
$foo = undef;
if ($foo) { print "Yes, it's true.\n"; }
if (not $foo) { print "No, it's false.\n"; }
-- [ e d @ h a l l e y . c c ]
| [reply] [d/l] [select] |
Re: When warnings get in the way
by davidrw (Prior) on Apr 22, 2005 at 16:53 UTC
|
The workaround seems fine -- it's straightforward and legible. You could probably make sure that $foo is never undef, too, so you don't have to worry about it. Something like:
my $foo = getFoo() || '';
if ($foo =~ /bar/ ){ ... }
But this has limitations (like if getFoo() returns 0).
side note #1 -- might just be your example, but treating foo as a boolean instead of string matching for "true" might work better, and avoids the warning. something like (again, depends on what getFoo() can return):
my $foo = getFoo() ? 1 : 0;
if ( $foo ){ ... }
side note #2 -- I'm sure someone will mention it if it's true, but i think perl6 has some new operators that can handle things like that (but i might be mis-remembering).
Update: I was thinking of (found it here) this:
$a // $b; # short for: defined($a) ?? $a :: $b
$pi //= 3;
# so could do (of course perl6 only):
if( $foo // '' eq "true" ){ ... }
# or
my $foo = getFoo();
$foo //= '';
if( $foo eq "true" ){ ... }
| [reply] [d/l] [select] |
|
| [reply] |
Re: When warnings get in the way
by itub (Priest) on Apr 22, 2005 at 17:56 UTC
|
I have an ambivalent relationship with "uninitialized" warnings, because they result in false positives and get in the way more often than not. Sometimes I just start my script with:
#!/usr/bin/perl
use strict;
use warnings;
no warnings 'uninitialized';
But for maximum safety you should leave them on, or just turn them off at a smaller scope. | [reply] [d/l] |
|
Yup, quite agree with that. For instance when grabbing data from a database, some columns may be empty, though initializing the hash before fetching the data would be a real pain. So I often use something like
no warnings;
print Dumper(\%bighash);
use warnings;
but your
no warnings 'uninitialized';
is a neat trick, maybe I'll try this one next time. | [reply] [d/l] [select] |
Re: When warnings get in the way
by Old_Gray_Bear (Bishop) on Apr 22, 2005 at 17:05 UTC
|
my $foo = " ";
Add six characters, three of them blank; there you go. It's not much of an effort, but in six months the maintenance programmer (and that might be you) is going to appreciate it.
By initializing the variable to a known state, you have told the Maintainer what you thought about the potential values that $foo can contain, and explicitly selected one of them as the default. Developers who think about the small things are more likely to be thinking about the bigger issues as well. It has been my unfortunate experience that people who do not initialize their variables often have other unpleasant coding habbits.
----
I Go Back to Sleep, Now.
OGB
| [reply] [d/l] |
|
That (and there was a similar suggestion above as well) doesn't help his problem.
my $foo = ' '; # or my $foo = '';
$foo = getFoo();
if ( $foo eq "true" ){ ... } # this will warn if $foo is undef
$foo = $dbh->selectrow_array("select x from blah limit 1");
if ( $foo eq "true" ){ ... } # this will warn if $foo is undef
In these cases, the original default value doesn't matter because it's overwritten later by a 'third party' (some sub or dbh call), and the hint to the Maintainer doesn't help cause it's something else (e.g. some db value) that's being stored. | [reply] [d/l] |
|
$foo = getFoo() || "";
I really need to start paying attention.
| [reply] [d/l] |
Re: When warnings get in the way
by jZed (Prior) on Apr 22, 2005 at 17:12 UTC
|
I will sometimes use this kind of construct if I have reasons for not defining $foo earlier.
if ( ($foo||'') eq "true") { ...
| [reply] [d/l] |
Re: When warnings get in the way
by kwaping (Priest) on Apr 22, 2005 at 17:28 UTC
|
As with most things in Perl, there are many ways to accomplish this task (avoiding the "uninitialized" error). Personally, the solution I use is contextual.
When doing form data validation where there can be a lot of fields, and all the field names may not neccessarily be known by the programmer, I like to use the OP's method:
if ($form{$key} && $form{$key} =~ /\w+/) { # ...
However, when checking data passed to a method or subroutine, I prefer to use the ||= (aka "default") method:
my ($self,$asdf) = @_;
$asdf ||= '';
Finally, in general, I try to scope, declare, and initialize my variables at the start of the program whenever possible. | [reply] [d/l] [select] |
Re: When warnings get in the way
by ambrus (Abbot) on Apr 22, 2005 at 17:31 UTC
|
You can turn on a certain kind of warning for a block or function,
like
if (do { no warnings "uninitialized"; $foo eq "true" }) { ... }
IMO, there is nothing wrong with disabling a warning,
as long as you do it only to certain warnings,
not all (like no warnings;), and
only for the part of code where the warning is ok.
This is especially important with warnings such as
no warnings "exiting";
for which there is no such easy way to avoid.
In this case however, I see nothing wrong with the explicit
$foo && test, I do not think it is really bad.
For a more comprehensible code, I'd write something like
if (defined($foo) && $foo eq "true") { ... }
or
if (($foo || "") eq "true") { ... }
or even
if ((defined($foo) ? $foo : "") eq "true") { ... }
| [reply] [d/l] [select] |
Re: When warnings get in the way
by dragonchild (Archbishop) on Apr 22, 2005 at 17:35 UTC
|
| [reply] |
Re: When warnings get in the way
by holli (Abbot) on Apr 22, 2005 at 16:54 UTC
|
Just define your variables. Use my $foo = ""; instead of my $foo;
| [reply] [d/l] [select] |
Re: When warnings get in the way
by salva (Canon) on Apr 24, 2005 at 12:45 UTC
|
for a one time construction I would use
if (defined $foo and $foo eq 'true') {...}
but if I needed to code similar tests in several places I would go for:
sub is_true ($) { defined $_[0] and $_[0] eq 'true' }
if (is_true $foo) {...}
if (is_true $bar) {...}
| [reply] [d/l] [select] |