Еще о переменных

Завершая разговор о типах данных Perl и переменных, мы решили в этом разделе суммировать полученные нами знания об использовании переменных в Perl и несколько расширить их, поговорив немного подробнее об операции присваивания в свете специфического для языка понятия — контекст.
Напомним, что переменные позволяют «хранить» в программе данные, которые требуются для реализации алгоритма решения некоторой задачи, и обращаться к ним с помощью имен переменных. Так как реально все данные хранятся в выделенной для них области памяти (мы не рассматриваем здесь данные, сохраняемые на внешних запоминающих устройствах — дисках, лентах и т. п.), то каждую переменную можно рассматривать как именованную область памяти, которая не только позволяет получить доступ к данным по имени, но и задает тип хранимых в ней данных, что определяет их формат хранения и набор применимых к ним операций.
В Perl определить тип переменной можно заданием специального префикса перед идентификатором переменной: $ — для скалярных переменных, @ — для массивов скаляров и % — для ассоциативных массивов. Для каждого типа переменных интерпретатор создает собственное пространство имен, храня в нем идентификаторы объявленных переменных соответствующего типа. Подобная технология при водит к тому, что в языке могут сосуществовать переменные разных типов с одним и тем же идентификатором: и скалярная $var, и массив скаляров @var, и хэш-массив %vаr. Скажем более, так как имя любой переменной должно начинаться с определенного символа, то в качестве идентификаторов переменных можно использовать и зарезервированные слова. Однако использовать подобную практику не рекомендуется. Лучше в качестве идентификаторов выбирать слова, отражающие смысл использования переменной в программе.
ВНИМАНИЕ. В языке Perl определен большой набор специальных предопределенных переменных, которые используются различными стандартными функциями для получения необходимой информации, а также для реализации некоторых языковых конструкций. Например, для передачи параметров в подпрограммы используется специальный массив скаляров @_, а для получения параметров, заданных при запуске интерпретатора, массив @ARGV.
Основное назначение переменных — хранение данных, над которыми в программе выполняются определенные действия, или операции. Следовательно, переменные могут использоваться в качестве операндов операций в выражениях. И здесь следует остановиться на контексте, в котором вычисляются операции. В Perl определено несколько контекстов, которые влияют на выполнение большинства операций и, естественно, на их результаты. В зависимости от контекста вычисления операндов операций и самого выражения результат может существенно отличаться и по-разному интерпретироваться. Два основных контекста — это скалярный и списковый. Смысл этих двух контекстов достаточно прост: первый из них требует, чтобы операнд операции или результат вычисления выражения интерпретировался как скаляр, а второй — как список. Например, если левым операндом операции присваивания является скалярная переменная, то правый операнд должен вычисляться в скалярном контексте, то есть его результатом должна быть скалярная величина. Соответственно, если левый операнд является массивом скаляров или хэш-массивом, то правый операнд вычисляется в списковом контексте, то есть он должен предоставить список вычисленных значений. Таким образом, в зависимости от используемого контекста переменные базовых типов будут возвращать разные значения.
Конечно, в первую очередь это относится к массивам Perl, так как скаляр в любом из упомянутых контекстов будет возвращать единственное значение, а вот массив нет. В списковом контексте, например, массив скаляров возвращает список значений своих элементов, а в скалярном — количество элементов.
В Perl, как уже отмечалось неоднократно, определен большой набор стандартных функций, которые рассматриваются как некоторые операции. Для них определено, какие значения они возвращают в списковом или в скалярном контексте, а также в каком контексте вычисляются их параметры, которые рассматриваются как операнды соответствующих операций.
Для переменных важной операцией является операция присваивания, которая, будучи завершена точкой с запятой, образует простой оператор присваивания:
переменная = выражение;
Используемая в левой части этой операции переменная определяет контекст, в котором вычисляется операнд-выражение правой части: скалярная переменная создает скалярный контекст, а переменная массива скаляров или хэш-массива — списковый контекст.
Конструктор массива скаляров в списковом контексте будет иметь значение, представляющее собой все значения элементов списка в заданном порядке. В скалярном контексте он вычисляется равным значению последнего элемента списка. Это связано с тем, что в этом контексте символ-разделитель элементов списка запятая, рассматривается как операция последовательного вычисления перечисленных через запятую выражений, возвращающая значение последнего вычисленного выражения. (Это полностью соответствует семантике операции «запятая» в языках С и C++, откуда она была заимствована разработчиками Perl.) Таким образом, следующие операторы будут по-разному выполняться в программе Perl:
@var = (1, 3, 5); # Массив @var содержит три элемента:
                  #  1, 3, 5 
$var = (1, 3, 5); # Скаляр $var содержит число 5
Аналогично ведет себя и конструктор хэш-массива, если учесть, что операция =>, обычно используемая в его конструкторе, является всего лишь синонимом операции «запятая»:
%var = ("first" => 5); # Хэш %var содержит один элемент 5
                       # с ключом first 
$var = ("first" => 5); # Скаляр $var содержит число 5
Конструктор массива можно использовать и в левой части операции присваивания. В этом случае, однако, все его элементы должны представлять правильные l-значения. Этим термином обозначается любая величина, которой можно присвоить значение, то есть использовать в левой части оператора присваивания (от английского термина «lvalue» — сокращение от «left value»). Использование конструктора массива в левой части оператора присваивания предписывает вычислять правый операнд в списковом контексте, что позволяет осуществлять множественное присваивание в одном операторе:
@m = (10, 20);
($а, $b) = @m; # $а = 10,  $b = 20
Если количество элементов левого списка больше количества элементов правого в операции множественного присваивания, то переменные левого списка, которым не хватило значений, не будут определены (их значения будут равны пустой строке""). Если правый список содержит больше элементов, чем левый список, то лишние значения просто не используются.
@m = (10, 20);
($a, $b, $c) = @m;    # $a = 10,  $b = 20, $с = ""
@m[2..3] = (30, 40);  # Добавили в массив @m еще
                      # два элемента
($а, $b, $с) = @m;    # $a = 10,  $b = 20,  $с = 30
                      # Значение 40 не используется
Эти примеры также показывают, что в списковом контексте, создаваемом для правого операнда операции присваивания конструктором массива левого операнда, массив скаляров вычисляется в виде списка значений своих элементов. В скалярном контексте массив скаляров будет вычисляться равным количеству содержащихся в нем элементов, что удобно для определения числа его элементов:
$a = @m; 	# $a = 4
Хэш-массивы, как и массивы скаляров, по-разному вычисляются в списковом и скалярном контексте.
В списковом контексте они «возвращают» список, состоящий из элементов всех пар ключ/значение:
%hash = (red => 1, green => 2, blue => 3, black => 4); 
@list = %hash; # @list = (blue, 3, green, 2, black, 4, red, 1)
При этом невозможно предсказать последовательность расположения пар ключ/ значение в вычисляемом списке, так как хэш-массивы хранят свои элементы в последовательности, наиболее удобной для доступа к ним. Эту ситуацию можно наблюдать в приведенном выше фрагменте, если распечатать полученные значения элементов массива @list (в комментарии к оператору присваивания показаны эти значения, полученные на нашем компьютере).
В скалярном контексте хэш-массив вычисляется в виде строки, содержащей два целых числа, разделенных символом наклонной черты. Последнее число показывает количество выделенных для хранения элементов хэш-массива участков памяти, а первое — количество использованных участков. Например, для хэш-массива, созданного в предыдущем фрагменте сценария Perl, получим следующий результат:
$scalarHash = %hash;   # $scalarHash = "3/8"
Любая переменная Perl может находиться в одном из двух состояний: определена и не определена. Если ей присвоено какое-либо значение, то такая переменная считается определенной, в противном случае — неопределенной. В выражениях неопределенная переменная вычисляется по-разному в зависимости от используемого контекста. Если при запуске интерпретатора установлен ключ -w, то использование в выражениях неопределенных переменных приведет к отображению предупреждающих сообщений.
Перед использованием переменной в выражении можно проверить ее «определенность» функцией defined, которая возвращает единицу, если переменная определена, и пустую строку в противном случае:
$var = 4;
defined($var); # Возвращает 1
defined(@var); # Возвращает "",
               # массив @var не инициализирован
В любой момент определенную переменную можно сделать неопределенной с помощью функции undef, передав в качестве параметра имя соответствующей переменной:
@array = (10, 20);
defined(@array); # Возвращает 1 - массив определен
undef(@array);
defined(@array); # Возвращает "" - массив не определен
ПРИМЕЧАНИЕ Заметим, что сделанная неопределенной переменная, естественно, теряет присвоенное ей ранее значение.
Завершая разговор о переменных, коротко обсудим такие понятия, как область видимости и время жизни переменной. Область программы, откуда можно обратиться к переменной, называется областью видимости переменной. В наших примерах все переменные были глобальными — они доступны из любой части программы: блока или подпрограммы. Perl позволяет создавать переменные, доступ к которым возможен только из определенной части программы. Это так называемые локальные переменные, и их область видимости ограничена блоками и подпрограммами. Для локальных переменных можно использовать имена, совпадающие с именами глобальных переменных, так как они создаются только на время выполнения блока или подпрограммы, в которых они определены. После завершения выполнения операторов подпрограммы или некоторого блока локальные переменные уничтожаются и становятся недоступными. При очередном входе в блок или вызове подпрограммы они снова создаются и становятся доступными. Более подробно мы будем изучать локальные переменные в главах 5 и 10.

Следующая страница Содержание главы


Реклама