Следующий фрагмент ищет индекс первого вхождения значения в массиве. Однако, когда скобки вокруг $index удалены, он работает неправильно. Что я делаю неправильно?
my ($index) = grep { $array[$_] eq $search_for } 0..$#array;
Следующий фрагмент ищет индекс первого вхождения значения в массиве. Однако, когда скобки вокруг $index удалены, он работает неправильно. Что я делаю неправильно?
my ($index) = grep { $array[$_] eq $search_for } 0..$#array;
Скобки меняют контекст, в котором grep
оценивается из скалярного контекста в контекст списка. В скалярном контексте grep
возвращает количество раз, когда выражение было истинным. В контексте списка он возвращает элементы, для которых выражение истинно.
Ниже показано, что разностный контекст:
my $x = grep {/foo/} @array; # the number of things that match /foo/
my ($x) = grep {/foo/} @array; # the first thing that matches /foo/
my @foo = grep {/foo/} @array; # all the things that match /foo/
my (@foo) = grep {/foo/} @array; # all the things that match /foo/
Скобки заключают для grep
. grep
будет фактически возвращать список элементов, для которых выражение истинно, а не только количество раз, когда выражение было истинным.
Я думаю, что вы ищете first_index
из List::MoreUtils:
use List::MoreUtils qw( first_index );
# ...
my $index = first_index { $_ eq $search_for } @array;
Функция grep ведет себя по-разному в контексте списка и в скалярном контексте. Это описано в perldoc -f grep
:
Вычисляет BLOCK или EXPR для каждого элемента LIST (локально установка $_ для каждого элемента) и возвращает значение списка состоящий из тех элементов, для которых выражение к истине. В скалярном контексте возвращает количество раз выражение было правдой.
Вы можете дублировать это самостоятельно с плохо названной функцией wantarray:
sub my_grep {
my $sub = shift;
my @return;
for my $item (@_) {
push @return if $sub->($item);
}
return @return if wantarray;
return scalar @return;
}
grep
возвращает список. Когда вы помещаете имя скалярной переменной в круглые скобки, Perl обрабатывает это целое значение l как список, поэтому он присваивает этому значению первое значение в списке.
Если у вас были другие скаляры в круглых скобках, вы получите значения второго, третьего и т.д. из grep
возвращаемого массива в них:
my ($index, $foo, $bar) = grep { $array[$_] eq $search_for } 0..$#array;
Кроме того, я думаю, что использование grep только для того, чтобы найти первый экземпляр, немного неэффективно, так как ему еще нужно пройти и запустить обратный вызов для каждого элемента массива. Особенно, если ваш массив длинный, вам может быть лучше писать цикл или использовать List:: MoreUtils, как указано выше.