What is the best way to check arguments provided to subroutines to compile with strict ( my solution works but there is a better way of doing it ? )
I have a large old library, I want to correct the code so I can add use strict; to it, most of my error are like this (on a page load it generates 189 errors):
Use of uninitialized value in string eq at ../lib/cgilibtest.pl line 1510
Use of uninitialized value in concatenation (.) or string at ../lib/cgilibtest.pl line 1511.
They are generated by subroutines writen like this:
#!/usr/bin/perl -w
sub example()
{
my $var1 = shift @_;
my $var2 = shift @_;
my $var3 = shift @_;
if($var2 eq ""){var2 = "something";} # this line generates the first type of error beacause $var2 is not defined
return "var1 = ".$var1.", var2 = ".$var2.", var3 = ".$var3; # this line generates the second type of error, beacause $var3 is not defined
}
print "Content-type: text/html\n\n";
$someVar = &example("firstVar");
print $someVar;
My solution was to use the ternary operator and write them as:
#!/usr/bin/perl -w
use strict;
sub example()
{
my $var1 = ($_[0])?$_[0]:"";
my $var2 = ($_[1])?$_[1]:"something";
my $var3 = ($_[2])?$_[2]:"";
return "var1 = ".$var1.", var2 = ".$var2.", var3 = ".$var3;
}
print "Content-type: text/html\n\n";
my $someVar = &example("firstVar");
print 开发者_如何学JAVA$someVar;
in PHP I use function example ($var1, $var2 = null, $var3 = null){...}
P.S. If someone knows a good tutorial or manual about "write strict code in perl" please leave a link to it.
Thank you.
- I think the PBP (Perl Best Practices) is a good read on a "strict" style. If
you follow what it says, you will at least be writing code compliant with
strict
andwarnings
.
What I see is uninit warnings. And, uninit warnings come with warnings
, not strict
. Though it usually goes hand in hand with strict.
For your particular error, it's important to realize that when you shift the arg array (which I have a tendency to call "deck") that the user of the method/function might not have passed anything in that position. Thus this is a good idiom;
my $var1 = shift || '';
''
is not uninitialized, and so won't get the warning. Ternaries are also a good means and I use them enough in my production code.
Of course, there's another issue though. If you take a look at the code above, what am I expecting? I'm expecting that $var1
might not have been passed any data. If that is the case, and I still want to stringify it--and there are enough cases like this in the local code--sometimes you just do this:
# don't bug me about uninitialized stringed values
# just concatenate them
no warnings 'uninitialized';
return "var1 = ".$var1.", var2 = ".$var2.", var3 = ".$var3;
It's equivalent code to doing this with a bunch of variables:
my $var = shift || '';
The idea is not to be taken by surprise with uninitialized data--having a policy that fits the instance, rather than slavishly coding to a standard.
Since your sub is easy enough, the above code will work. But you probably want to make it do more in the future, so I suggest the following construct to isolate the parts that we're okay with uninitialized values and where you don't want to be caught unaware.
{ no warnings 'uninitialized';
$string = ...;
}
$obj_that_should_be_defined->do_something_objecty( $string );
do
blocks are also useful for this.
$obj_that_should_be_defined->do_something_objecty(
do { no warnings 'uninitialized'; sprintf( ... ); }
);
Of course anything that you pass as a variable, you can also filter with a map
. The following works if you just want to take care of undefs.
$object->method( map { defined() ? $_ : '' } @anon_args );
- I good word of warning is that anything that hasn't already been checked in the scope--anything coming from somewhere else: parameter, IO, regex capture, returns from functions--can be uninitialized. The practice of good coding should have you checking all these things IF you care in that instance whether they are
undef
of not.
Now, a lot of times people check for undefined variables just to croak. Since Perl's messages have gotten better, and perhaps if they could improve in the future. It might not be necessary to croak specifically.
my $feldman = Feldman->new( qw<here is some data for you> )
or die 'Could not create Feldman!'
;
$feldman->dont_just_sit_there_do_something();
However without the explicit or die
, the code above will definitely die if $feldman
is undefined. You can't call a method on an undefined value, so there is case for not specifically checking. If $feldman
is undefined, then you didn't create it, there is just one more level of inference you have to make from undefined to the error message. Neither tells us why the object was not created, just that it wasn't. (The constructor should at least have carped about something it didn't have.)
sub example {
my $var1 = shift || '';
my $var2 = shift || 'something';
my $var3 = shift || '';
return "var1 = ".$var1.", var2 = ".$var2.", var3 = ".$var3;
}
I would second the recommendation to look at Perl Best Practices. Damian Conway is a great teacher. Also bear in mind that the book is not intended to be read dogmatically, and even suggestions that you don't adopt are worth thinking over.
For positional arguments that all take the same default value, I often do something like this:
sub example {
my ($var1, $var2, $var3) = map { defined() ? $_ : '' } @_;
}
Even more flexible, however, is to write functions that use named arguments instead. This approach has several benefits, one of them being the ease with which default values can be handled:
sub example {
my %DEFAULTS = ( foo => 0, bar => '', fubb => 'blah' );
# When the hashes are merged, user-supplied arguments
# will trump the defaults.
my %args = (%DEFAULTS, @_);
}
example(foo => 123);
If I recall correctly, PBP discusses this trick of merging hashes, which is useful in many contexts.
精彩评论