Скалярный и список контекста в Perl

1 @backwards = reverse qw(yabba dabba doo);
2 $backwards = reverse qw(yabba dabba doo);
3 
4 print @backwards; #gives doodabbayabba
5 print $backwards."\n"; #gives oodabbadabbay
6 print @backwards."\n"; #gives 3

В приведенном выше коде почему line 6 дать 3 в качестве выхода? Почему он конвертируется в скалярный контекст, если он соединен с \n?

Спасибо

3 ответа

Решение

Ваш вопрос в конечном итоге "почему @backwards находится в скалярном контексте в строке 6", и возникает вопрос: "Как я могу определить контекст термина?".

Контекст определяется термином "что вокруг" (то есть его "контекст").

Как я могу определить контекст термина? Посмотрев на оператора / функцию, которая использует термин.

Какие шаги вы могли бы предпринять, чтобы самостоятельно выяснить контекст для @backwards, если у вас не было полезных ребят из stackru, чтобы рассказать вам о его контексте?

Здесь мы имеем

print @backwards."\n"

так что есть два оператора / функции. Как мы узнаем, какой из них предоставляет контекст для @backwards? Консультируясь с приоритетом. В верхней части perlop.pod у нас есть таблица приоритетов Perl (print - это "оператор списка"):

left    terms and list operators (leftward)
...
left    + - .
...
nonassoc    list operators (rightward)

О, отлично, теперь нам нужно знать, идет ли печать влево или вправо. Изучив раздел "Термины и операторы списка (влево)" в perlop (сразу после списка приоритетов), мы видим, что print находится здесь справа, потому что мы не заключили его аргументы в скобки.

Таким образом, конкатенация имеет более высокий приоритет, поэтому конкатенация предоставляет контекст для @backwards.

Следующим шагом является проверка документов (снова perlop) для объединения:

Binary "." concatenates two strings.

Строки являются скалярами, поэтому двоичные "." объединяет два скаляра.

И у нас наконец есть это!

@backwards имеет скалярный контекст, потому что конкатенация предоставляет скалярный контекст каждому из его операндов.

Woo. Это было легко, не так ли:-)

Строка 6 дает 3, потому что . оператор накладывает скалярный контекст как на свои операнды, так и на @backwards в скалярном контексте дает 3 (потому что любой массив в скалярном контексте сообщает о количестве элементов в нем).

Пытаться:

print "@backwards\n";

Чтобы объединить элементы в массиве, используйте join:

print join(', ', @backwards) . "\n";

Контекст хитрый. И иногда пара пикселей может иметь все значение. Рассмотрим следующие две строки:

print localtime, "\n";
print localtime. "\n";

Вы заметили разницу?

Эти два утверждения выглядят действительно похожими. Но то, что происходит в двух случаях, очень разное.

В первом утверждении print передается список аргументов. И документация для печати говорит, что она навязывает контекст списка своим аргументам. Поэтому вызов localtime оценивается в контексте списка, и функция возвращает список чисел.

Во втором утверждении print получает единственный аргумент. Этот аргумент является строкой, созданной путем объединения результатов вызова localtime с символом перевода строки. Оператор конкатенации накладывает скалярный контекст на оба своих операнда. Поэтому вызов localtime возвращает текущее время в удобочитаемом формате строки. Затем эта строка объединяется с новой строкой, и полученная строка передается на печать. Вызов print все еще оценивает свой аргумент в контексте списка, но этот контекст не имеет значения для способа интерпретации однострочного аргумента.

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

Другие вопросы по тегам