Неявное создание ссылок

В рассмотренных случаях осуществляется явное создание ссылки при помощи операции "\" или специальных синтаксических конструкций. В них всегда явным образом определяется скалярная переменная, в которую и заносится значение ссылки. Ссылка может также создаваться неявно в случае, когда операция разыменования применяется к ссылке, ранее не созданной в программе явным образом, и в контексте выражения предполагается, что такая ссылка должна существовать. Возможно, последнее предложение звучит не совсем понятно. Его смысл станет ясным в следующем разделе.

 

9.3. Разыменование ссылок

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

 

9.3.1. Разыменование простой скалярной переменной

Если ссылка на некоторый объект: скалярную переменную, массив, ассоциативный массив и т. д., является простой скалярной переменной без индексов, то для обращения к самому объекту применяется правило: вместо имени объекта подставить в выражение простую скалярную переменную, содержащую ссылку. Например:

1 $а = $$scal_ref;

2 @b = @$arr_ref;

3 %с = %$hash_ref;

4 &f - &$code_ref;

5 $$d[0] = 7;

6 $$h{"one"} = 1;

Здесь предполагается, что переменная $scal_ref содержит ссылку на скалярную величину, $arr_ref - ссылку на массив, $hash_ref - ссылку на ассоциативный массив, $code_ref — ссылку на подпрограмму.

Рассмотрим подробно пятую строку.

Во-первых, следует определить, что является ссылкой: скалярная переменная $d, указывающая на анонимный массив, или элемент $d[0] массива @d. Ответ содержится в сформулированном выше правиле разыменования. Поскольку в строке 5 применяется именно оно, то индексированная переменная $d[0] ссылкой быть не может. Ссылкой является простая скалярная переменная $d, которая используется в качестве имени. Из контекста видно, что на ее месте должно стоять имя массива, следовательно, $d является ссылкой на анонимный массив

Во-вторых, здесь мы имеем пример неявного создания ссылки, о котором говорилось в предыдущем разделе. Ссылка $d не была ранее создана явным образом, но ее существование предполагается в операции присваивания. Поэтому компилятор создаст ссылку $d на анонимный массив, поместит в нее адрес массива и по этому адресу сохранит значение первого элемента, равное 7.

Все сказанное можно отнести к шестой строке с единственным отличием: вместо ссылки на анонимный массив здесь фигурирует ссылка $ь на анонимный хеш-массив.

 

9.3.2. Блоки в операциях разыменования ссылок

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

${$d[0]} = 7; ${$h{"one"}} = 1; ${&f()}[l] = 3;

Разберем первую строку. Начальный символ $ является признаком скалярной переменной, за которым должно следовать ее имя. Вместо имени используется блок, следовательно, выражение внутри блока интерретируется как ссылка. В данном случае осуществляется разыменование ссылки $d[0], являющейся элементом массива @а. Аналогично, во второй строке осуществляется обращение к скалярной переменной, на которую указывает ссылка $h{"one"}, являющаяся элементом ассоциативного массива %ь. В третьей строке блок, возвращающий ссылку, состоит из одного обращения к функции f (). Ее значение интерпретируется как ссылка на массив, и второму элементу этого массива присваивается значение 3.

 

9.3.3. Операция разыменования "->"

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

$<$а[0]'}[1] = 17; , ${$Ь[0]}{"опе"} = 1;

В первой строке осуществляется обращение к отдельному элементу массива массивов, во второй — к отдельному элементу массива хеш-массивов.

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

Несколько упростить запись и улучшить наглядность можно, используя операцию "->" ("стрелка").

Аргумент слева от стрелки может быть любым выражением, возвращающим ссылку на массив или хеш-массив.

Если левосторонний аргумент является ссылкой на массив, то аргумент справа от стрелки — индекс, заключенный в квадратные скобки и определяющий элемент этого массива.

Если левосторонний аргумент является ссылкой на хеш-массив, то аргумент справа от стрелки — значение ключа, помещенное в фигурные скобки и определяющее элемент этого хеш-массива.

Результатом операции "->" является соответственно значение элемента массива или хеш-массива. Предыдущий пример можно более компактно записать в виде

$а[0]->[1] = 17; $Ь[0]->{"опе"} = 1;

Конструкция $а[0]->[1] обозначает второй элемент массива, определяемого ссылкой $а[0]. Конструкция $ь[0]->{"опе"} обозначает элемент, соответствующий ключу "one" хеш-массива, задаваемого ссылкой $Ь[0].

Вообще, если $arr_ref — ссылка на массив, то $arr_ref->[$i] обозначает i-й элемент этого массива. Если $hash_ref — ссылка на хеш-массив, то $hash_ref->{"key"} обозначает элемент этого хеш-массива, соответствующий ключу "key".

Если бы в последнем примере вместо именованных массивов @а и @ь использовались ссылки на массив, например, $ref_a и $ref_b, то соответствующие операции присваивания имели вид

$ref_a->[0]->[l] = 17; $ref_b->[0]->{"one"} = 1;

Здесь мы снова сталкиваемся с неявным созданием ссылок. По контексту элемент массива $ref_a->[0] должен быть ссылкой на массив, а $ref_b->[0] — ссылкой на хеш-массив. Обе ссылки ранее не были определены, но их существование предполагается в контексте выражения. Данные ссылки будут созданы автоматически.

Операция "->" позволяет для обращения к отдельному элементу составного массива или хеш-массива использовать более простые выражения, например,

$a[$i]->[$j]->[$k] вместо${${$a[$i]}[$j]}[$k], $b[$i]->{"key"}->[$j] вместо${${$b[0]}{"key"}}[$j]

и т. д.

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

и хеш-массивов, можно опустить символы "->" между квадратными и/или фигурными скобками, содержащими индексы или ключи элементов. Предыдущие выражения примут еще более простой вид: $a[$i][$j][.$k] и $b[$i] {"key"} [$j]соответственно.

 

9.4. Символические ссылки

Из предыдущего раздела мы знаем, что если ссылка не определена, но ее присутствие требуется контекстом, то она создается автоматически. Если же определенная ранее скалярная величина не является ссылкой, но используется в качестве ссылки, то ее называют символической ссылкой. Значение символической ссылки интерпретируется как имя некоторой переменной. Над этой переменной будут выполняться все операции, применяемые к символической ссылке. Вспомним, что значением жесткой ссылки является адрес. В следующем примере переменная $name_a используется как символическая ссылка на переменную $а.

1 $name_a = "a";

2 $$name_a = 17;

3 @$name_a = (1,2,3);

4 $name_a->[3] = 4;

5 %$name_a = ("one"=>l, "two"=>2, "three"=>3);

6 &$name_a () ;

В строке 2 переменной $а присваивается значение 17. В строке 3 определяется и инициализируется массив @а с элементами (1,2,3). В строке 4 к массиву @а добавляется четвертый элемент со значением 4. В строке 5 инициализируется хеш-массив %а. В строке 6 осуществляется вызов функции а о (предположим, что такая функция существует).

Символическая ссылка может указывать только на переменную, имя которой содержится в таблице символов пакета.

(О пакетах и таблицах символов описано в главе 12.)

Лексические переменные, определяемые при помощи функции ту (), в таблицу символов не входят, поэтому их имена невидимы для механизма, реализующего символические ссылки.

(О лексических переменных и применении функции ту () рассказывается в главе П.) Для иллюстрации рассмотрим следующий пример:

$name_a="a"; { my $a="Hello!";

print $$name_a; };

Здесь переменная $name_a используется в качестве символической ссылки на переменную $а, и можно предположить, что результатом выполнения этой последовательности будет вывод строки "Hello!". В действительности переменная $а является невидимой для символической ссылки, поскольку она определена как лексическая переменная внутри блока {...}. Поэтому в результате выполнения данного фрагмента будет напечатана пустая строка.

Применение символических ссылок является потенциально опасным из-за возможности возникновения смысловых ошибок. Например, может показаться, что в результате выполнения следующей последовательности операторов

1 $а[0]="Ь";

2 #..............

3 $Ь[0]=2;

4 $Ь[1]=2;

5 #..............

6 $а[0] [0]=0;

7 #..............

8 $prod = $Ь[0]*Ь[1];

переменная $prod получит значение 4. Но это не так. В строке 6 мы осуществляем присваивание, рассчитывая на то, что будет применен известный механизм неявного создания жесткой ссылки $а [0]. Мы "забыли" о том, что значение $а[0] уже использовалось в строке 1 и, следовательно, в строке 6 элемент массива $а[0] является символической ссылкой, указывающей на переменную с именем "Ь". Это имя будет подставлено вместо символической ссылки, в результате чего элемент массива ь[0] получит новое значение 0. В итоге значение переменной $prod будет равно 0.

Во избежание подобных ошибок можно запретить использование символических ссылок в пределах текущего блока при помощи директивы

use strict 'refs';

Это ограничение, если требуется, можно отменить для внутреннего блока при помощи другой директивы

no strict 'refs1;

(Директивы use, no рассматриваются в главе 12.)

Еще одно замечание, касающееся символических ссылок. В версии 5.001 появилась новая возможность: если переменную, являющуюся символической ссылкой, заключить в фигурные скобки, то такая конструкция интер-

претируется не как символическая ссылка, а как значение переменной, подобно тому, как аналогичная конструкция интерпретируется командной оболочкой shell операционной системы UNIX. В следующем фрагменте

1 use strict 'refs';

2 ${name};

3 ${"name"};

вторая строка представляет собой просто значение переменной $name, а третья строка интерпретируется как символическая ссылка, указывающая на переменную $name и вследствие применения директивы use strict 'refs1 вызывает сообщение об ошибке вида

Can't use string ("name") as a SCALAR ref while "strict refs" in use

 

9.5. Использование ссылок

В данном разделе мы рассмотрим некоторые примеры, связанные с основным применением ссылок — конструированием структур данных. В качестве первой структуры построим массив массивов или двумерный массив. Для примера рассмотрим массив @calendar, содержащий календарь, например, на 2000 год. Значением элемента $caiendar[$i] [$j] является название дня недели, приходящегося на(j+l)-ft день (i+l)-ro месяца, i=(0..11),j=(0..30)(pHc. 9.1).

Рис 9.1. Структура массива @calendar

 

9.5.1. Замыкания

Для заполнения массива ecaiendar нам потребуется функция, которая по заданному году, месяцу и дню месяца вычисляет соответствующий день недели. Читатель может сам написать свой вариант функции, может быть, более изящный. Мы предлагаем наш вариант (пример 9.1) с основной целью: рассказать об одном интересном свойстве анонимных подпрограмм. Кроме того, он правильно работает для любого года нашей эры.

Вычисление дня недели основано на том, что:

  • 1 января 1 года нашей эры было понедельником;
  • каждый год, номер которого делится на 4, является високосным, за исключением тех номеров, которые делятся на 100 и не делятся на 4.

(Вопросы создания функций пользователем рассмотрены в главе 11.)

sub GetDay (

my $year = shift;

my @days = (Q,31,59,90,120,151,181,212,243,273,304,334);

my @week = ("Monday","Tuesday","Wednesday","Thursday",

"Friday","Saturday","Sunday"); my $previous_years_days = ($year -1 ).*365 + int (($year-l) /4)

- int(($year-l)/100) + int(($year-l)/400); return sub { my ($month, $day)=@_;

my $n « $previous_years_days + $days[$month-l] + $day -1; $n++ if ($year%4 == 0 and $year%100 != 0 or

$year%400 == 0 and $month > 2) ; return $week[$n%7]; } };.

Аргументами функции GetDay о являются номер года, номер месяца и номер дня месяца. Внутри тела функции им соответствуют переменные $уеаг, $month и $day. Функция подсчитывает число дней $п, прошедших с 1 января 1 года. Остаток от деления этого числа на 7 — $п%7 — определяет день недели как элемент массива $week[$n%7].








Дата добавления: 2016-04-23; просмотров: 494;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.03 сек.