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

Oh great MonksWhoKnowStuffIHaveNotYetFiguredOut, this is probably really basic, but it keeps coming up and poking me and I can't seem to find a decent solution. As an example, the following code:

use strict; use warnings; my @data=( [1,2,3], [4,5,6], [7,8], [10,11,12] ); my @categories=qw/a b c/; my @master; foreach my $d(@data){ my %h; foreach my $c(0 .. $#categories){ my $val=$d->[$c]; my $cat=$categories[$c]; $h{$val}=$cat; } push @master, \%h; }

produces this error:

Use of uninitialized value $val in hash element at ./test.pl line 23.

Now, this is certainly a trivial case, but it keeps happening to me over very large lists of data that are read in from a file. Typically there's a typo in the data of some kind.

What I end up having to do is build a bunch of code to trap that specific error, based on a guess as to what caused it, and possibly in conjunction with printing each statement to STDERR along with a line number. Doing this on a semi-regular basis causes me to think unpure thoughts, and is probably having a negative effect on my Eternal Karma.

What I would like is an easy way to know the line number in the file (or the array index I've slurped it into), and the actual incoming data that annoyed Perl, so I can fix it in the data file. Knowing the problem line in the code is only partially useful when it's a data problem.

An appropriate liquid sacrifice to the monastary will be made in an appropriate manner later, but may be upgraded based on the quality of answers.

Replies are listed 'Best First'.
Re: Errors on loops over data
by BrowserUk (Patriarch) on Sep 16, 2009 at 18:04 UTC

    0 .. $#categories runs through 0, 1, & 2; but the third element of your @data only contains 2 elements: [7,8], so when you do my $val = $d->[$c]; on the third iteration of the outer loop, $val gets set to undef hence the error when you try to use $val as a hash key in line 23.

    The solution is "don't do that" :)

    if( defined $val ) { $h{$val}=$cat; } else { ## Do something else (or nothing). }

    What alternative action you should take is very much dependant upon your application requirements.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Errors on loops over data
by ELISHEVA (Prior) on Sep 16, 2009 at 18:11 UTC

    The best way to get line numbers is to get out of the habit of slurping. It is also better for memory consumption and not terribly difficult to do. Instead read line by line from the file and Perl will print out the data line number for you when you have an error.

    use strict; use warnings; my @categories=qw/a b c/; my @master; #replace this line with a while statement+chomp+split #foreach my $d(@data){ while (my $line = <DATA>) { chomp $line; my $d = [ split(m{,}, $line) ]; my %h; foreach my $c(0 .. $#categories){ my $val=$d->[$c]; my $cat=$categories[$c]; $h{$val}=$cat; } push @master, \%h; } __DATA__ 1,2,3 4,5,6 7,8 10,11,12

    prints out

    Use of uninitialized value in hash element at YourScript.pm line 17, < +DATA> line 3.

    Best, beth

Re: Errors on loops over data
by Anonymous Monk on Sep 16, 2009 at 17:47 UTC
    What I would like is an easy way to know the line number in the file (or the array index I've slurped it into), and the actual incoming data that annoyed Perl, so I can fix it in the data file. Knowing the problem line in the code is only partially useful when it's a data problem.

    Why don't you check?

    if ( my $cat = $categories[$c] ){ $h{$val}=$cat; }
Re: Errors on loops over data
by EvanCarroll (Chaplain) on Sep 16, 2009 at 17:44 UTC
    I'm not sure /why/ you want to assign to $h{undef} which is what your trying, my best suggestion would be to explicitly use $h{ $val // '__NOT PRESENT__' } in perl 5.10. This still leaves it as an exercise to the reader, on what to do when you have two undefs you want to save too...
    This is pseudo-data, which probably doesn't represent a real world problem very well. My fix would be to skip over the undef vals.
    foreach my $d(@data){ my %h; foreach my $c(0 .. $#categories){ my $val=$d->[$c]; next unless defined $val; my $cat=$categories[$c]; $h{$val}=$cat; } push @master, \%h; }


    Evan Carroll
    The most respected person in the whole perl community.
    www.EvanCarroll.com