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

Dear Monks,

I have a 2D array, and I want to filter it by keeping the lines where first column matches a pattern (the variable $to_keep). It works fine, with the following code :

my @matrix_filtered = grep { $_->[0] =~ /$to_keep/ } @matrix;

The result is consistent with what I aim to do, but I get the following warning (with Perl perl5.26.3) : "Use of uninitialized value in pattern match (m//)".

Using m/$to_keep/ inside the grep makes the same result.

It's actually quite annoying for me, because my script handles millions of lines, and these warnings dilute the real errors I could get... How could I get rid of this?

Thank you a lot in advance!

  • Comment on "Use of uninitialized value in pattern match (m//)" warning, doing a grep on 2D array
  • Download Code

Replies are listed 'Best First'.
Re: "Use of uninitialized value in pattern match (m//)" warning, doing a grep on 2D array
by hippo (Archbishop) on Oct 14, 2022 at 14:06 UTC

    Works fine for me on 5.34:

    #!/usr/bin/env perl use strict; use warnings; use Test::More tests => 1; use Test::NoWarnings; my @matrix = ( [1, 2], [3, 4] ); my $to_keep = 3; my @matrix_filtered = grep { $_->[0] =~ /$to_keep/ } @matrix;

    Perhaps your data do actually have uninitialized values. See also How to ask better questions using Test::More and sample data.


    🦛

      Thank you, you must be right, but argh I can't see them... I included elsewhere some "defined" in my script such as "if (defined($variable)..." that work fine, but here I have troubles pointing uninitialized values in my matrix. I suppressed the warning with "no warnings 'uninitialized'", but it's problematic for the other errors I could get.

      I tried : my @matrix_defined = grep {defined} @matrix; with no success.

        Your code as presented in the OP only filters on the first column, so that's the one to use in this pre-filter - if that's the way you want to go with this.

        #!/usr/bin/env perl use strict; use warnings; use Test::More tests => 3; use Test::NoWarnings; my @matrix = ( [1, 2], [undef, 9], [3, 4] ); my $to_keep = 3; my @matrix_defined = grep { defined $_->[0] } @matrix; is $#matrix_defined, 1, '2 rows after prefilter'; my @matrix_filtered = grep { $_->[0] =~ /$to_keep/ } @matrix_defined; is $#matrix_filtered, 0, '1 row after full filter';

        🦛

Re: "Use of uninitialized value in pattern match (m//)" warning, doing a grep on 2D array
by kcott (Archbishop) on Oct 14, 2022 at 19:17 UTC

    G'day soblanc,

    It's not clear whether undef values have erroneously crept into your data and you want to fix that; or if undef values are quite valid and you simply want to skip them when filtering.

    You haven't shown how you create @matrix so some guesses are needed here. The code below is effectively in two parts:

    1. A variety of ways that undef values could have crept into your data. This is by no means a complete list of possibilities. It should give you hints as to what to look for when fixing the data.
    2. How to skip undef values when filtering. Based on your last post (Sort a matrix by row), and "my script handles millions of lines" (in this post) it looks like you're dealing with biological data; accordingly, you'll want to pay attention to code efficiency — not just for this small snippet but for your entire program. grep EXPR, LIST (which I used) is faster than grep BLOCK LIST (which you used): see grep. You can test that for yourself with Benchmark.
    #!/usr/bin/env perl use v5.26.3; use warnings; my @matrix; my ($col2, $col3) = qw{column2 column3}; my $keep_re = qr{1}; my $fmt = "%d: %-5s %s %s\n"; { my $col1; push @matrix, [$col1, $col2, $col3]; } { my $col1 = []; push @matrix, [$col1->[0], $col2, $col3]; } { my @col1s; my $elem1; push @col1s, $elem1; push @matrix, [$col1s[0], $col2, $col3]; } { my @col1s = (1); pop @col1s; push @matrix, [$col1s[0], $col2, $col3]; } { my @col1s = (2); my $elem1; unshift @col1s, $elem1; push @matrix, [$col1s[0], $col2, $col3]; } for (1, 2, 12, 100, 234) { push @matrix, [$_, $col2, $col3]; } say '*** Original:'; my @original = @matrix; for (0 .. $#original) { $original[$_][0] //= 'undef'; printf $fmt, $_, @{$original[$_]}; } say '*** Filtered:'; my @filtered = grep +(defined $_->[0] and $_->[0] =~ $keep_re), @matrix; for (0 .. $#filtered) { $filtered[$_][0] //= 'undef'; printf $fmt, $_, @{$filtered[$_]}; }

    Output:

    *** Original: 0: undef column2 column3 1: undef column2 column3 2: undef column2 column3 3: undef column2 column3 4: undef column2 column3 5: 1 column2 column3 6: 2 column2 column3 7: 12 column2 column3 8: 100 column2 column3 9: 234 column2 column3 *** Filtered: 0: 1 column2 column3 1: 12 column2 column3 2: 100 column2 column3

    While I'm not advocating the use of "no warnings 'uninitialized';" in this instance, I will point out that the effect of the warnings pragma is lexically scoped. When it's appropriate, turn off warnings in as small a scope as possible.

    use warnings; ... # all warnings in effect here { no warnings 'XYZ'; # all warnings except XYZ in effect here } # all warnings in effect here

    — Ken

Re: "Use of uninitialized value in pattern match (m//)" warning, doing a grep on 2D array
by ikegami (Patriarch) on Oct 14, 2022 at 14:18 UTC

    That means that $_->[0] is undefined, which means the first value of one of the array references in @matrix is undefined.

    You can work around the warning by checking if $_->[0] is defined or not. But since you don't seem to be expecting it to be undefined, there might be a bug somewhere and that would just mask the bug.

    Possible problem: You might have autovivified an element of @matrix by accessing it when you shouldn't have.

Re: "Use of uninitialized value in pattern match (m//)" warning, doing a grep on 2D array
by BillKSmith (Monsignor) on Oct 15, 2022 at 15:28 UTC
    You have already received several good suggestions on how to suppress your warnings messages. I strongly suggest that you do not adopt any of them until you know what is causing the undefined data. It may be a symptom of a more serious problem. In that case, fixing the underlying problem would probably eliminate the warnings. Only hide the warnings if you know that the undefined data fields are valid and that your grep handles them properly. If you know that they are invalid and you cannot remove them sooner, use hippo's pre-filter (Re^3: "Use of uninitialized value in pattern match (m//)" warning, doing a grep on 2D array) to remove them.
    Bill
Re: "Use of uninitialized value in pattern match (m//)" warning, doing a grep on 2D array
by Anonymous Monk on Oct 14, 2022 at 17:20 UTC

    Under the assumption that you do not want rows with undefined values in your output (however they got there):

    my @matrix_filtered = grep { defined( $_->[0] ) && $_->[0] =~ /$to_keep/ } @matrix;
    

    You could simply silence the warning, but then you will get the bogus rows in your output if '' =~ /$to_keep/ is true.