Почему мой Perl grep не возвращает первый матч? - PullRequest
18 голосов
/ 11 августа 2009

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

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

Ответы [ 6 ]

43 голосов
/ 11 августа 2009

Скобки изменяют контекст, в котором 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/
8 голосов
/ 11 августа 2009

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

7 голосов
/ 12 августа 2009

Я думаю, что вы ищете first_index из Список :: MoreUtils :

use List::MoreUtils qw( first_index );

# ...

my $index = first_index { $_ eq $search_for } @array;
5 голосов
/ 12 августа 2009

Функция 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;
}
1 голос
/ 12 августа 2009

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

1 голос
/ 11 августа 2009

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

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

my ($index, $foo, $bar) = grep { $array[$_] eq $search_for } 0..$#array;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...