ОЧЕНЬ важная часть, которую пропустили другие ответы, заключается в том, что строка
grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;
фактически изменяет аргументы, передаваемые в функцию, а не их копии.
grep
является командой фильтрации, а значение в $_
внутри блока кода является псевдонимом одного из значений в @_
.@_
в свою очередь содержит псевдонимы аргументов, передаваемых функции, поэтому, когда оператор s///
выполняет свою подстановку, выполняется изменение исходного аргумента.Это показано в следующем примере:
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
Требуемое поведение (применить функцию, которая изменяет $_
к копии списка) было инкапсулировано как функция apply
вколичество модулей.Мой модуль List :: Gen содержит такую реализацию.apply
также довольно просто написать самому:
sub apply (&@) {
my ($sub, @ret) = @_;
$sub->() for @ret;
wantarray ? @ret : pop @ret
}
С этим ваш код может быть переписан как:
sub natural_sort {
apply {s/(^|\D)0+(\d)/$1$2/g} sort apply {s/(\d+)/sprintf"%06.6d",$1/ge} @_
}
Если ваша цель с повторными заменами состоит в том, чтобы выполнитьПри сортировке исходных данных с примененной переходной модификацией, вам следует рассмотреть идиому Perl, известную как преобразование Шварца , которая является более эффективным способом достижения этой цели.