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

I am getting following error every time I code in perl. Don't know hwy this is coming and what I am missing. I have placed small code sample below. This code is also giving me same error. As far as I get I am doing mistakes in traversing arrays

Error: Use of uninitialized value in print at printArrayWithOutLoop.pl line 20 (#1) (W uninitialized) An undefined value was used as if it were already defined. It was interpreted as a "" or a 0, but maybe it was a mistake. To suppress this warning assign a defined value to your variables. To help you figure out what was undefined, perl will try to tell you the name of the variable (if any) that was undefined. In some cases it cannot do this, so it also tells you what operation you used the undefined value in. Note, however, that perl optimizes your program and the operation displayed in the warning may not necessarily appear literally in your program. For example, "that $foo" is usually optimized into "that " . $foo, and the warning will refer to the concatenation (.) operator, even though there is no . in your program.

This code will print each number in integer array in new line without using for or while loop.

use strict; use warnings; use diagnostics; use 5.010; my @inputArray = (1,2,3,4,5,6,7,8); my $inputArrayLen = @inputArray; my $counter = 0; &traverseArray($inputArrayLen); sub traverseArray{ if($counter<=$inputArrayLen){ print $inputArray[$counter]; print "\n"; $counter++; traverseArray($inputArrayLen); } }
Thanks all for finding error and teaching those things.

Replies are listed 'Best First'.
Re: Use of uninitialized value in print at line 20 (#1)
by davido (Cardinal) on Feb 25, 2016 at 06:12 UTC

    $inputArrayLen is set equal to 8. There are 8 elements in the array. You start counting from 0. You keep going until $counter is greater than $inputArrayLen.

    So you print elements 0, 1, 2, 3, 4, 5, 6, 7... all of which are fine. Element 0 contains the value 1. Element 7 contains the value 8. What does element 8 contain? An undefined value. Element 8 is the 9th element in the array.

    Change <= to < and it's fixed. This is an off by one error -- a common result of forgetting about zero-based numbering.

    You have some other problems, but they're not actually causing trouble. One is how you pass parameters. You never actually use the parameter passed into the subroutine. Instead, you just absorb the same variable because it happens to be within the same lexical scope. Have a look at perlsyn for a better understanding of scoping and parameter passing. Minimally, it should follow this pattern:

    foo($value); sub foo { my ($v) = @_; print "$v\n"; }

    Also, it seems like your goal is a recursive solution. Usually in exercises where recursion is a goal, it's preferable to store state within the subroutine's parameters. Here's an example.

    my @inputArray = (1,2,3,4,5,6,7,8); traverseArray2(\@inputArray); sub traverseArray2 { my ($aref, $counter) = (shift, shift // 0); return if $counter >= @$aref; print $aref->[$counter], "\n"; traverseArray2($aref, $counter+1); }

    You can even get rid of the counter and eliminate the call-stack overhead like this:

    my @inputArray = (1,2,3,4,5,6,7,8); traverseArray3(@inputArray); sub traverseArray3 { return unless @_; print shift, "\n"; goto &traverseArray3; }

    Dave

      Thanks David. Still new to coding and Perl but your suggestions are priceless.
Re: Use of uninitialized value in print at line 20 (#1)
by GrandFather (Saint) on Feb 25, 2016 at 06:21 UTC

    The problem is you recurs 1 too many times. However, there is a swathe of smelly areas in your code. Take a look at the following code to see how it can be improved in various ways. You may want to ask questions about why various things have been done or how they work.

    use strict; use warnings; use 5.010; my @inputArray = (1, 2, 3, 4, 5, 6, 7, 8); traverseArray(\@inputArray, 0); sub traverseArray { my ($aRef, $index) = @_; return if $index >= @$aRef; print "$aRef->[$index]\n"; traverseArray($aRef, $index + 1); }
    Premature optimization is the root of all job security
Re: Use of uninitialized value in print at line 20 (#1)
by Laurent_R (Canon) on Feb 25, 2016 at 10:12 UTC
    Hi,

    as already mentioned by other monks, your direct problem is an off-by one error.

    I would like however to point to other serious problems in your code, although they happen to have no direct consequences in this specific case. <p Since your aim is apparently to "print each number in integer array in new line without using for or while loop", using recursion is a very natural solution. However, in general, recursive functions should in general depend only on arguments passed to them and should not rely on global variables. Especially your use of the $counter global variable is a recipe for disaster when things get a bit more complicated. Also, the way you're using $inputArrayLen is a mistake, since you're passing as a parameter to your recursive subroutine, but, since you're not using the parameters passed to the sub, you are actually using a global copy of that value.

    You could fix it this way:

    use strict; use warnings; my @inputArray = (1..8); my $counter = 0; traverseArray($counter); sub traverseArray { my $counter = shift; return if $counter >= @inputArray; print "$inputArray[$counter]\n"; traverseArray($counter+1); }
    This solution still has one global variable, i.e. the @inputArray array. To me, this is not a major problem and you could live with that, but, ideally, it would be better to get rid of it and also pass the array as an argument. It could be something like this:
    # same initialization as before traverseArray($counter, @inputArray); sub traverseArray { my ($counter, @input) = @_; return if $counter >= @input; print "$input[$counter]\n"; traverseArray($counter+1, @input); }
    That's cleaner and that's OK in theory, but that is not very efficient because that makes a local copy of the array each time the subroutine is called, and that can be an overhead (in terms of momory size and speed performance), especially if the array is large.

    There are various ways to avoid that, but they are using some slightly advanced concepts (e.g. references or closures) which you probably would want to learn a bit later. Both solutions above are already a good starting point.

Re: Use of uninitialized value in print at line 20 (#1)
by AnomalousMonk (Archbishop) on Feb 25, 2016 at 16:23 UTC
    my @inputArray = (1,2,3,4,5,6,7,8);
    my $inputArrayLen = @inputArray;

    Even given the recursive context in which the OPed question is couched, I'm a bit surprised that no one has yet mentioned the  $# array operator (if that's the correct terminology), which gives you the maximum index of the array. (Update: See perldata for a discussion of  $# and arrays.)

    As others have pointed out, evaluating an array in scalar context as in the
        my $inputArrayLen = @inputArray;
    statement yields the number of elements of the array. Using the  $# operator, the statement
        my $maxIndex = $#inputArray;
    gives you the highest index of any element in the array.
    Leaving aside any considerations of recursion, the idiomatic, "Perlish" way of iterating over an array is something like
        my @array = (...);
        ...
        for my $element (0 .. $#array) {
            do_something_with($element);
            }
    Leaving aside any considerations of recursion, a better way of iterating over an array is something like
        my @array = (...);
        ...
        for my $i (0 .. $#array) {
            do_something_with($array[$i]);
            }
    and better yet IMHO, the idiomatic, "Perlish" way would be
        for my $element (@array) {
            do_something_with($element);
            }

    Of course, there are other ways, such as using the built-in functions map and grep, of iterating over arrays, but those are other stories for other days.

    Update: Fixed the brainfart for-loop example.


    Give a man a fish:  <%-{-{-{-<