Скалярные переменные
В этой части Учебника Perl мы рассмотрим структуры данных, существующие в Perl и узнаем, как их использовать.
В Perl 5 есть 3 основных структуры данных. Это скаляры, массивы и хэши. В других языках последние также могут называться словарями, справочными таблицами или ассоциативными массивами.
Перед переменными в Perl всегда ставится знак, называемый "сигил". Этими знаками являются $ для скаляров, @ для массивов, и % для хэшей.
Скаляр может содержать единичное значение, например число или строку. Также он может содержать ссылку на другую структуру данных, к чему мы обратимся позже.
Название скалярной переменной всегда начинается с $ (знак доллара), за которым следуют буквы, числа и подстрочия. В качестве названия переменной может использоваться $name или $long_and_descriptive_name. Также встречается вариант $LongAndDescriptiveName, который обычно называют "CamelCase", но в Perl-сообществе обычно предпочитают названия, состоящие только из символов нижнего регистра, с подстрочиями, отделяющими слова.
Поскольку мы всегда используем strict, нам всегда нужно объявлять переменные с помощью my. (Позже вы также узнаете об our и некоторых других способах, но пока что будем использовать только объявление через my.) Мы можем либо присвоить переменной значение сразу, как в этом примере:
use strict; use warnings; use 5.010; my $name = "Foo"; say $name;
либо сначала объявить ее, а присвоить значение позже:
use strict; use warnings; use 5.010; my $name; $name = "Foo"; say $name;
Предпочтительно использовать первый способ, если логика кода позволяет.
Если мы объявили переменную, но еще не присвоили ей значение, она содержит значение, называемое undef, которое схоже с NULL в базах данных, но имеет немного отличающееся поведение.
Можно проверить, является ли переменная undef с помощью функции defined:
use strict; use warnings; use 5.010; my $name; if (defined $name) { say 'defined'; } else { say 'NOT defined'; } $name = "Foo"; if (defined $name) { say 'defined'; } else { say 'NOT defined'; } say $name;
Можно присвоить для скалярной переменной значение undef:
$name = undef;
Скалярные переменные могут содержать числа или строки. То есть я могу написать:
use strict; use warnings; use 5.010; my $x = "hi"; say $x; $x = 42; say $x;
и это сработает.
Как это работает вместе с операторами и перегрузкой операторов в Perl?
По большей части Perl устроен диаметрально противоположно другим языкам. Вместо того, чтобы операнды определяли, как работают операторы, оператор определяет, как ведут себя операнды.
Так что, если у меня есть две переменные, содержащие числа, оператор в итоге решает, будут ли они восприниматься как числа или как строки:
use strict; use warnings; use 5.010; my $z = 2; say $z; # 2 my $y = 4; say $y; # 4 say $z + $y; # 6 say $z . $y; # 24 say $z x $y; # 2222
+, оператор числового сложения, прибавляет два числа, так что и $y и $z ведут себя как числа.
., оператор конкатенации строк, так что $y и $z ведут себя как строки. (В других языках это бы могло называться строковым сложением.)
x, оператор повторения, повторяет строку слева количества раз, определяемое числом справа, так что в этом случае $z действует как строка, а $y - как число.
Результаты были бы теми же, если бы обе переменные создавались как строки:
use strict; use warnings; use 5.010; my $z = "2"; say $z; # 2 my $y = "4"; say $y; # 4 say $z + $y; # 6 say $z . $y; # 24 say $z x $y; # 2222
И даже если одна из них создана как число, а вторая как строка:
use strict; use warnings; use 5.010; my $z = 7; say $z; # 7 my $y = "4"; say $y; # 4 say $z + $y; # 11 say $z . $y; # 74 say $z x $y; # 7777
Perl автоматически конвертирует числа в строки и наоборот, как того требует оператор.
Мы называем это числовым и строковым контекстом.
Вышеупомянутые случаи довольно просты. Преобразование числа в строку - по сути просто заключение его в кавычки. Преобразование строки в числа, когда она состоит только из цифр - простой случай. То же будет, если в ней присутствует десятичная точка, например "3.14". Вопрос в том, что если в строке содержатся символы, не образующие число? Например "3.14 is pi". Как тогда она себя поведет в числовых операциях (то есть в числовом контексте)?
Это тоже несложно, хотя может потребовать некоторого объяснения.
use strict; use warnings; use 5.010; my $z = 2; say $z; # 2 my $y = "3.14 is pi"; say $y; # 3.14 is pi say $z + $y; # 5.14 say $z . $y; # 23.14 is pi say $z x $y; # 222
Когда строка попадает в числовой контекст, Perl смотрит на левую часть строки, и пытается превратить ее в число. Пока это получается, эта часть становится числовым значением переменной. В числовом контексте (+) строка "3.14 is pi" считается числом 3.14.
В каком-то смысле это совершеннейший прозвол, но так это работает, так что мы живем с этим.
Код выше также выдаст предупреждение в стандартный канал ошибок (STDERR):
Argument "3.14 is pi" isn't numeric in addition(+) at example.pl line 10.
в случае, если вы использовали use warnings, что настоятельно рекомендуется. Использование предупреждений поможет заметить, если что-то пойдет не совсем так, как предполагалось. Надеюсь, результат выполнения $x + $y теперь вполне ясен.
За кадром
На всякий случай, perl не конвертирует $y в 3.14, он просто использует числовое значение для сложения. Думаю, это объясняет и результат $z . $y. В этом случае perl использует исходное строковое значение.
Возможно, вы удивлены, что $z x $y выдает 222, хотя справа у нас 3.14. Дело в том, что perl может повторить строку только целое число раз... В этой операции perl молча округляет число справа. (Если задуматься, можно понять, что "числовой" контекст, о котором шла речь раньше, на самом деле включает в себя несколько подконтекстов, один из которых - "целочисленный". В большинстве случаев perl делает то, что подсказывает здравый смысл большинству людей-непрограммисов)
Кроме того, мы даже не видим предупреждения "partial string to number conversion", как в случае с +.
Это не из-за другого оператора. Если закомментировать сложение, мы увидим это предупреждение уже на этой операции. Причина отсутствия второго предупреждения в том, что когда perl создал числовое значение строки "3.14 is pi", он сохранил его в потайном кармане переменной $y. Так что по сути $y с тех пор имеет и строковое, и числовое значение, и нужное будет использоваться в соответствующие моменты уже без преобразования.
Есть еще три вещи, которые я хотел бы упомянуть. Первая это поведение переменной со значением undef, вторая - fatal warnings, и третья - как избежать автоматического перевода строк в числа.
undef
Если у меня в переменной содержится undef, что для большинства значит "ничего", ее все равно можно использовать. В числовом контексте она будет 0, а в строковом - пустой строкой:
use strict; use warnings; use 5.010; my $z = 3; say $z; # 3 my $y; say $z + $y; # 3 say $z . $y; # 3 if (defined $y) { say "defined"; } else { say "NOT"; # NOT }
С двумя предупреждениями:
Use of uninitialized value $y in addition (+) at example.pl line 9. Use of uninitialized value $y in concatenation (.) or string at example.pl line 10.
Как можно видеть, в конце скриппта переменная все еще undef, так что условное выражение выдаст "NOT".
Fatal warnings
Второе, о чем я хотел сказать - некоторые предпочитают, чтобы приложение выбрасывало жесткое исключение вместо мягкого предупреждения. Если вы тоже исповедуете этот принцип, в начале скрипта можно написать
use warnings FATAL => "all";
С этим скрипт выведет число 3, а затем выбросит исключение:
Use of uninitialized value $y in addition (+) at example.pl line 9.
Это то же сообщение, которое раньше было предупреждением, но теперь после этого выполнение скрипта останавливается. (Если, конечно, исключение не было поймано, но об этом мы поговорим в другой раз.)
Избежание автоматического перевода строки в число
Если вам не хочется, чтобы строки автоматически переводились в числа там, где невозможен полный перевод, вы можете проверять, выглядит ли строка как число, когда получаете ее из внешнего мира.
Для этого нам нужно подгрузить модуль Scalar::Util и содержащуюся в нем подпрограмму looks_like_number.
use strict; use warnings FATAL => "all"; use 5.010; use Scalar::Util qw(looks_like_number); my $z = 3; say $z; my $y = "3.14"; if (looks_like_number($z) and looks_like_number($y)) { say $z + $y; } say $z . $y; if (defined $y) { say "defined"; } else { say "NOT"; }
перегрузка операторов
Наконец, можно перегрузить операторы так, чтобы операнды указывали, что должен делать оператор, но эту тему мы оставим в качестве продвинутой.
Published on 2013-09-20