Почему Perl shift complain "Тип arg 1 для смещения должен быть массивом (а не итератором grep). '?

У меня есть структура данных, которая является хешем, который содержит массив хэшей. Я бы хотел туда добраться и вытащить первый хэш, который соответствует значению, которое я ищу. Я пробовал это:

   my $result = shift grep {$_->{name} eq 'foo'} @{$hash_ref->{list}};

Но это дает мне эту ошибку: Type of arg 1 to shift must be array (not grep iterator). Я перечитал perldoc для grep, и я думаю, что У меня все имеет смысл. grep возвращает список, не так ли? Это неправильный контекст?

Я сейчас использую временную переменную, но я хотел бы выяснить, почему это не работает.

Ответ 1

Список не является массивом.

my ($result) = grep {$_->{name} eq 'foo'} @{$hash_ref->{list}};

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

Ответ 2

Я думаю, что лучший способ написать это:

use List::Util qw/first/;

my $result = first { $_->{name} eq 'foo' } @{ $hash_ref->{list} };

Не только будет более ясно, что вы пытаетесь сделать, но и быстрее, потому что он остановит grepping вашего массива, как только он найдет соответствующий элемент.

Ответ 3

Другой способ сделать это:

my $result = (grep {$_->{name} eq 'foo'} @{$hash_ref->{list}})[0];

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

my $result = (grep $_->{name} eq 'foo', @{$hash_ref->{list}})[0];

"Конструкторы значений списка" в perldata документы подписи списков:

Значение списка также может быть индексировано как обычный массив. Вы должны поместить список в круглые скобки, чтобы избежать двусмысленности. Например:

# Stat returns list value.
$time = (stat($file))[8];

# SYNTAX ERROR HERE.
$time = stat($file)[8];  # OOPS, FORGOT PARENTHESES

# Find a hex digit.
$hexdigit = ('a','b','c','d','e','f')[$digit-10];

# A "reverse comma operator".
return (pop(@foo),pop(@foo))[0];

Как я помню, мы получили эту функцию, когда Рэндал Шварц в шутку предложили ее, и Chip Salzenberg, который в то время был патч-машиной, - реализовал его в тот вечер.

Обновление: Немного поиска показывает, что я имел в виду $coderef->(@args). Сообщение фиксации даже регистрирует разговор!