Принуждение контекста списка в Perl

Я пытаюсь понять контекст Perl, и я споткнулся о камень;

С учетом кода;

#!/usr/bin/perl

my $b = (33,22,11);
print "$b\n";

my $b = () = (33,22,11);
print "$b\n";

my @b = (33,22,11);
print "@b\n";

my @b = () = (33,22,11);
print "@b\n";

Результаты (последняя строка пуста);

 11  

 3 

 33 22 11

 <>

Поскольку второй отпечаток вернул длину списка, я предполагал, что где-то массив был сгенерирован, так как массив в скалярном контексте оценивает его длину. Но четвертый отпечаток, похоже, опровергает это предположение. Я ожидал увидеть " '33 22 11' но вместо этого ничего не получил. Что здесь происходит?

Ответ 1

Вы сбиты с толку, потому что думаете = - это единственный оператор, в то время как это может привести к двум различным операторам: оператору присваивания списка или скалярному оператору присваивания. Мини-учебник: Scalar vs List Assignment Operator объясняет различия.

my $b = (33,22,11);
------------------            Scalar assign in void context.
        ----------            List literal in scalar context. Returns last.

my @b = (33,22,11);
------------------            List assign in void context.
        ----------            List literal in list context. Returns all.

my $b = ( () = (33,22,11) );
---------------------------   Scalar assign in void context.
        -------------------   List assign in scalar context. Returns count of RHS
               ----------     List literal in list context. Returns all.

my @b = ( () = (33,22,11) );
---------------------------   List assign in void context.
        -------------------   List assign in list context. Returns LHS.
               ----------     List literal in list context. Returns all.

Что касается вашего заголовка, принудительный контекст списка невозможно для каждого. Если функция возвращает список, когда ожидается скаляр, это приводит к дополнительным значениям в стеке, что приводит к тому, что операторы получают неправильные аргументы.

Вы можете, однако, сделать что-то вроде:

( EXPR )[0]

или

( EXPR )[-1]

EXPR в контексте списка, но целое возвращает только один элемент возвращаемого списка.

Ответ 2

Назначение списка в скалярном контексте возвращает количество элементов в правой части (случай 2). Случай 4 сначала назначает (33, 22, 11) - (), а затем присваивает () (значение которого не изменилось) на @b.

Ответ 3

Отвечая на неявный вопрос в заголовке:

Perl предоставляет встроенный scalar для принудительного скалярного контекста, когда он иначе не использовался; аналогичного list builtin не существует, я предполагаю, во многом потому, что это может означать несколько разных вещей, например:

list { code to execute in list context };

может быть определено:

sub list (&) { scalar( () = $_[0]->() ) } # return count of elements
sub list (&) { ( $_[0]->() )[0] } # return first element
sub list (&) { ( $_[0]->() )[-1] } # return last element
sub list (&) { for( $_[0]->() ) {} } # return nothing

Отвечая на вопрос:

Если вы путаете контекст, помните, что операторы накладывают контекст на операнды, а не наоборот. В вашем проблемном случае у вас есть скалярное назначение, присваивание скаляру результата назначения списка, которое ставит назначение списка в скалярном контексте. Чтобы узнать, что происходит, просто посмотрите, что будет присваиваться списку в скалярном контексте. (В общем, операторы возвращают то, что делает наиболее здравым смыслом, а это означает, что правило не запоминается, которое применяется ко всем операторам.)