Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks!
I am passing values to a subroutine and sometimes in my code I don't need to pass all the values expected by the sub, and I know I am getting an "Use of uninitialized value..." from the expected values in the subroutine. OK here is the passing sample code:
... my $info = send_values( { name => $name, address => $address, id => $id, pic => $pic, } ); ...

Here is the sample sub code:
... sub send_values{ my ($args) = @_; #test: my $values = "Your Results are:\n $args->{name}\n $args->{address}\n $args->{pic}\n $args->{id}\n $args->{zip}\n $args->{phone}\n "; }

These two "$args->{zip} and $args->{phone}" will give me warnings on "use of uninitialized value" because I am not passing them. Can I use some sort of "if @_ defined" to prevent this issue?
Thanks for the help!

Replies are listed 'Best First'.
Re: Subroutine question on use of uninitialized value.
by toolic (Bishop) on Jun 01, 2012 at 13:34 UTC
    exists
    sub send_values { my ($args) = @_; my @params = qw( name address pic id zip phone ); my $values = "Your Results are:\n"; for (@params) { $values .= "$args->{$_}\n" if exists $args->{$_}; } }
      $ perl -we 'my $h={k=>undef}; print $h->{k} if exists $h->{k}' Use of uninitialized value in print at -e line 1.

      This is why defined() may be preferable.

      -sauoq
      "My two cents aren't worth a dime.";
      Would be possible to have it without a loop, I need to make a small xml with these values like:
      my $value = "<results> <name>$args->{name}</name> <address>$args->{address}</address> ... </results> ";
      I was thinking about checking right at this first line:
      my ($args) = @_;

      Thanks!
        Would be possible to have it without a loop
        What's wrong with loops? They're low in fat, high in fiber... oh, nevermind.
        use warnings; use strict; my $info = send_values({ name => 5, address => 6, id => 7, pic => 8, }); print $info; sub send_values { my ($args) = @_; my @params = qw( name address pic id zip phone ); my $values = "<results>\n"; for (@params) { $values .= " <$_>$args->{$_}</$_>\n" if exists $args->{$_ +}; } $values .= "</results>\n"; return $values; } __END__ <results> <name>5</name> <address>6</address> <pic>8</pic> <id>7</id> </results>

        You could do the above without a loop, but it wouldn't be as scalable or as easy to maintain. For example, you would have to type 'address' 4 times instead of once:

        $values .= " <address>$args->{address}</address>\n" if ex +ists $args->{address};
Re: Subroutine question on use of uninitialized value.
by sauoq (Abbot) on Jun 01, 2012 at 14:08 UTC
    These two "$args->{zip} and $args->{phone}" will give me warnings on "use of uninitialized value" because I am not passing them.

    Actually, that's not true. Perl doesn't care if you pass them or not. You are getting the warnings because you are attempting to use them. It's an important distinction. As has been pointed out, you can use exists(). That'll tell you if the key is in the hash... you might also consider defined(). If you want an empty tag or a default (rather than no tag) when the data doesn't exists, you can use a conditional operator...

    print '<zip>' . (defined($args->{zip}) ? $args->{zip} : "DEFAULT") . ' +</zip>';

    -sauoq
    "My two cents aren't worth a dime.";
Re: Subroutine question on use of uninitialized value.
by AnomalousMonk (Bishop) on Jun 01, 2012 at 15:50 UTC

    A slightly different approach is to define a default for every argument and explicitly make any required argument undefined. Then check for undefined arguments, take action as appropriate. This can generate a lot of useful warnings.

    >perl -wMstrict -le "print send_values({name => 'Joe Doe', addr => '123 4th St.'}); print send_values({ addr => '666 Mobil Ave.', phone => '321-432-5432' }); print send_values({ name => 'Moe Foe', addr => '7 6th Rd.', phone => '432-543-6543' }); ;; sub send_values { my ($hr_args) = @_; ;; my %defaults = ( name => undef, addr => undef, phone => '(not given)', ); ;; my %args = (%defaults, %$hr_args); my @bad = grep !defined($args{$_}), keys %args; return undef if @bad and warn((caller 0)[3], qq{: bad arg(s): @bad}); ;; return sprintf qq{<n>%s</n> <a>%s</a> <p>%s</p>}, @args{ qw(name addr phone) }; } " <n>Joe Doe</n> <a>123 4th St.</a> <p>(not given)</p> main::send_values: bad arg(s): name at -e line 1. Use of uninitialized value in print at -e line 1. <n>Moe Foe</n> <a>7 6th Rd.</a> <p>432-543-6543</p>
Re: Subroutine question on use of uninitialized value.
by davido (Cardinal) on Jun 01, 2012 at 18:09 UTC

    I noticed you were opposed to loops for some reason. I have a hard time imagining a good reason for a loopless stragegy. Here is how I would begin:

    sub send_values { my ($args) = @_; my $values = "Your Results are:\n"; $values = join '', map { exists $args->{$_} ? $args->{$_} . "\n" : () } qw( name address pic id zip phone ); # do whatever it is you want to do with $values. }

    You would still get an undefined value warning if the caller did something silly like: send_values( { name => undef } ); ...but that seems unlikely, and probably truly deserves a warning.

    If your reason for avoiding loops has to do with performance, I recommend abandoning that concern for the time being.


    Dave