The VERY important part that other answers have missed is that the line
grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;
Is actually modifying the arguments passed into the function, and not copies of them.
grep
is a filtering command, and the value in $_
inside the code block is an alias to one of the values in @_
. @_
in turn contains aliases to the arguments passed to the function, so when the s///
operator performs its substitution, the change is being made to the original argument. This is shown in the following example:
sub test {grep {s/a/b/g; 1} @_}
my @array = qw(cat bat sat);
my @new = test @array;
say "@new"; # prints "cbt bbt sbt" as it should
say "@array"; # prints "cbt bbt sbt" as well, which is probably an error
The behavior you are looking for (apply a function that modifies $_
to a copy of a list) has been encapsulated as the apply
function in a number of modules. My module List::Gen contains such an implementation. apply
is also fairly simple to write yourself:
sub apply (&@) {
my ($sub, @ret) = @_;
$sub->() for @ret;
wantarray ? @ret : pop @ret
}
With that, your code could be rewritten as:
sub natural_sort {
apply {s/(^|\D)0+(\d)/$1$2/g} sort apply {s/(\d+)/sprintf"%06.6d",$1/ge} @_
}
If your goal with the repeated substitutions is to perform a sort of the original data with a transient modification applied, you should look into a Perl idiom known as the Schwartzian transform which is a more efficient way of achieving that goal.