perlnatural-sortperl-critic

Where is $_ being modified in this perl code?


The following perl code generates a warning in PerlCritic (by Activestate):

sub natural_sort {
    my @sorted;
    @sorted = grep {s/(^|\D)0+(\d)/$1$2/g,1} sort grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;
}

The warning generated is:

Don't modify $_ in list functions

More info about that warning here

I don't understand the warning because I don't think I'm modifying $_, although I suppose I must be. Can someone explain it to me please?


Solution

  • Both of your greps are modifying $_ because you're using s//. For example, this:

    grep {s/(^|\D)0+(\d)/$1$2/g,1}
    

    is the same as this:

    grep { $_ =~ s/(^|\D)0+(\d)/$1$2/g; 1 }
    

    I think you'd be better off using map as you are not filtering anything with your greps, you're just using grep as an iterator:

    sub natural_sort {
        my $t;
        return map { ($t = $_) =~ s/(^|\D)0+(\d)/$1$2/g; $t }
               sort
               map { ($t = $_) =~ s/(\d+)/sprintf"%06.6d",$1/ge; $t }
               @_;
    }
    

    That should do the same thing and keep critic quiet. You might want to have a look at List::MoreUtils if you want some nicer list operators than plain map.