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

Dear Monks,

I'm very new to PERL (or programing) and wrote only one script so far. Even though the script runs fine, I have a warning and wish to ask your wisdom to understand it.

The purpose of the script is to parse a log and retrieve the relevant UserID's. After locating the userID in a line in the log I send it to the following sub (AddToUserArray) which inserts it to my user array (only if it wasn't inserted before).

The warning I receive is in the AddToUserArray sub. The warning is: "useless use of private variable in void context at ..." The line it points to is the sub line 18 (yes it only has brackets)

1 sub AddToUserArray { 2 my $UserID=$_[0]; 3 my $i=0; 4 my $Pexists=0; 5 my $Size; 6 7 if($UserID eq "") { # incase a bad UserID was recived. if Pexi +sts=1 it will skip &addtouserlog 8 $Pexists=1; 9 return $Pexists; 10 } 11 12 $Size=scalar(@Participants); 13 for($i;$i<$Size;$i++) { 14 if ($Participants[$i] eq $UserID) { 15 $Pexists=1; 16 last; 17 } 18 } 19 if ($Pexists==0) { 20 $Participants[$i]=$UserID; 21 } 22 return $Pexists; 23 }

Again, the entire script works fine but I want to understand this warning.

Thank you!

Replies are listed 'Best First'.
Re: Useless use of private variable in void context
by GrandFather (Saint) on Oct 11, 2009 at 20:36 UTC

    Your immediate problem has been addressed and you've received a tip to improve your code within the narrow context of the code you've shown. However there are some 'bigger picture' items that should be mentioned.

    First off, Perl has some really wizard stuff for handling arrays. grep and push turn the whole sub into:

    sub AddToUserArray { my ($UserID) = @_; # Like this because it scales well for many par +ams # return 1 to skip addtouserlog () # return if bad UserID return 1 if $UserID eq ""; # return if handled user already return 1 if grep {$_ eq $UserID} @Participants; push @Participants, $UserID; return; # Generally better to return undef than 0 }

    Note the last return which returns undef. Perl subs can return either a list or scalar value depending on calling context. undef works for most purposes in both contexts, but 0 becomes a 1 element list in list context and that can sometimes be surprising.

    However, where you need to look stuff up arrays are often a bad choice. In Perl the better choice is to use a hash. The following code provides some assumed context for the sub to demonstrate how that goes:

    use strict; use warnings; my %Participants; while (<DATA>) { chomp; my ($name, $tail) = split / /, $_, 2; next if ! defined ($name) || exists $Participants{$name}; $Participants{$name} = $tail; print "Added $name: $tail\n"; } __DATA__ Fred Red team Joe Blue Team Pete Blue Team Fred Red Team Annie Red Team Lee Green Team Steve Green Team

    Note that the whole sub has been replaced by just two lines:

    next if ! defined ($name) || exists $Participants{$name}; $Participants{$name} = $tail;

    The more important, and less obvious advantage, is that exists works in pretty much constant (and very short) time - no more looking through the entire array each time you need to see if an entry is there already. For even modest numbers of entries that is an overwhelming factor!


    True laziness is hard work
      Dear GrandFather,

      Thank you very much for your answer. I know I have still much to learn.

      I actually thought about using a hash table but since this is my first script I wanted to keep it as simple (for me) as possible.

      I'm thinking about rewriting it and if so I'll use a hash table and the perlish loop style.

      Thank you again!
Re: Useless use of private variable in void context
by zwon (Abbot) on Oct 11, 2009 at 19:07 UTC

    I reckon that's because of lonely $i in for($i;...) at line 13. If you replace it with

    for(;$i<$Size;$i++)
    or

    for(my $i=0; $i<$Size; $i++) no, that's not suitable for this case, thanks moritz for pointing this out.

    the warning will gone. Note that Perl is not C and there's no need to declare all variables in the beginning of the block.

      Using for(my $i=0; $i<$Size; $i++) is wrong here, because it means that $i will be 0 after the loop, while another part later on requires it to be scalar @Participants.

      Note that there's a much more perlish way to write the whole loop:

      for my $p (@Participants) { if ($p eq $UserID) { return 1; } }

      If the user ID was not found, $i will always be the array size. Which means that you can replace $Participants[$i]=$UserID; with push @Participants, $UserID;, which is a bit more readable.

      Perl 6 - links to (nearly) everything that is Perl 6.
        Dear Moritz,

        First of all thank you for your help.

        1. There's something that I'm not sure I understand, you say that when I use for ($i=0; $i<$size; $i++), after the loop ran a few times and than ended ($i was incremented a few times) it is set back to 0 once the loops ends? (You don't really have to answer that cause I'm testing it later today).

        2. Your perlish way of writing the loop is interesting. I only know a bit of C (one university class) and that is why I wrote the loop the way I did. Do you advice me to start writing in your format or should I stick to my old C style?

        Thank you so much for contributing your wisdom to a novice like me.

        okarmi
Re: Useless use of private variable in void context
by sflitman (Hermit) on Oct 11, 2009 at 19:11 UTC
    The problem is for($i;... as nothing is happening to $i there. Change it to $i=0, warning should go away. It is a little weird that the warning appears on the last line of the for-loop, but that's probably how the parsing worked out.

    HTH,
    SSF

Re: useless use of private variable in void context
by gmargo (Hermit) on Oct 11, 2009 at 19:14 UTC

    The first "$i" in the for loop causes the warning. "$i" at that point is a statement that does nothing and the resultant value is not used by anyone (normally an assignment occurs there) and hence the "useless use" warning.

    This program will generate the same warning:

    #!/usr/bin/perl use strict; use warnings; my $y = 9; $y; exit 0;
    Change the for loop to
    for(;$i<$Size;$i++) {
    and the warning will go away.