Подтвердить что ты не робот

Почему мой Perl grep не возвращает первое совпадение?

Следующий фрагмент ищет индекс первого вхождения значения в массиве. Однако, когда скобки вокруг $index удалены, он работает неправильно. Что я делаю неправильно?

my ($index) = grep { $array[$_] eq $search_for } 0..$#array;
4b9b3361

Ответ 1

Скобки меняют контекст, в котором 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/

Ответ 2

Скобки заключают для grep. grep будет фактически возвращать список элементов, для которых выражение истинно, а не только количество раз, когда выражение было истинным.

Ответ 3

Я думаю, что вы ищете first_index из List::MoreUtils:

use List::MoreUtils qw( first_index );

# ...

my $index = first_index { $_ eq $search_for } @array;

Ответ 4

Функция 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;
}

Ответ 5

grep возвращает список. Когда вы помещаете имя скалярной переменной в круглые скобки, Perl обрабатывает это целое значение l как список, поэтому он присваивает этому значению первое значение в списке.

Если у вас были другие скаляры в круглых скобках, вы получите значения второго, третьего и т.д. из grep возвращаемого массива в них:

my ($index, $foo, $bar) = grep { $array[$_] eq $search_for } 0..$#array;

Ответ 6

Кроме того, я думаю, что использование grep только для того, чтобы найти первый экземпляр, немного неэффективно, так как ему еще нужно пройти и запустить обратный вызов для каждого элемента массива. Особенно, если ваш массив длинный, вам может быть лучше писать цикл или использовать List:: MoreUtils, как указано выше.