Часто при программировании на Perl бывает необходимо использовать структуры данных более сложные, нежели скалярные переменные, массивы или ассоциативные массивы. Простейший пример таких структур — двумерная таблица, элементы которой являются скалярными значениями. Её элементы расположены по строкам и столбцам. Такие таблицы называют ещё двумерными массивами, поскольку каждый их элемент задаётся двумя индексами — номерами строки и столбца.
Наивные люди могут захотеть реализовать такие двумерные массивы как массив массивов. «Внешний» массив должен содержать строки таблицы, а эти строки, в свою очередь, будут «внутренними» массивами, в которых уже будут размещаться сами элементы таблицы.
Напомним, что правила языка Perl не позволяют размещать в массивах массивы. Элементами массива могут быть лишь скалярные значения. Попытка создать массив массивов не приведёт к желаемому результату, хотя и не вызовет ошибки:
Perl@pascalTriangle=( (1), (1, 1), (1, 2, 1), (1, 3, 3, 1), (1, 4, 6, 4, 1), (1, 5, 10, 10, 5, 1) );
Результат будет тем же самым, как если бы все элементы были бы записаны подряд, а не разделены по строкам таблицы:
Perl@pascalTriangle=(1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, 1, 5, 10, 10, 5, 1);
Произойдёт то, что называют линеаризацией (выстраивание элементов в линию), так что внутренние круглые скобки не произведут желаемого эффекта. Поэтому мы лишаемся удобного способа извлечь из треугольника Паскаля биномиальный коэффициент (число сочетаний из по ) по номеру строки и столбца . Треугольнику Паскаля у нас посвящена целая глава.
Хотя массив не может содержать другие массивы, он может содержать ссылки на массивы.
В Perl можно создавать и использовать ссылки на скалярные значения, массивы, ассоциативные массивы, процедуры, а также на иные объекты, о которых сейчас не время упоминать. Ссылки в Perl — это скалярные значения, в которых содержится информация о виде ссылки (на скаляр, на массив, ассоциативный массив или на процедуру), и, самое главное, адрес в памяти, по которому расположено адресуемое (то есть то, на которое указывает ссылка) значение.
Сильно забегая вперёд, скажем, что ссылки на различные структуры данных служат для представления объектов классов при объектно-ориентированном программировании (см. главу 38. «Объектно-ориентированное программирование»).
Ссылки в Perl могут быть созданы несколькими способами.
Ссылки на существующие переменные или процедуры создаются с помощью оператора адресования (\):
Perl$refScalar=\$scalar; в переменной$refScalar
окажется ссылка на скалярную переменную$scalar
$refArray=\@array; в переменной$refArray
окажется ссылка на массив@array
$refHash=\%hash; в переменной$refHash
окажется ссылка на ассоциативный массив%hash
$refProc=\&proc; в переменной$refProc
окажется ссылка на процедуруproc
Тот же оператор \ позволяет создать ссылки на значения выражений:
Perl$refString=\'школа'; ссылка на строку'школа'
$refNumber=\(2*27); ссылка на значение54
Не обязательно создавать переменную-массив или ассоциативный массив, чтобы создать ссылку на неё. Имеется возможность создавать ссылки на анонимные (безымянные) переменные.
Выражение, состоящее из списка значений, перечисленных через запятую в квадратных скобках, даёт ссылку на анонимный массив:
Perl$refPrimes=[2, 3, 5, 7, 11, 13, 17, 19, 23]; ссылка на массив — список нескольких простых чисел
Правильный способ создать структуру данных для хранения биномиальных коэффициентов в треугольнике Паскаля, будет таким:
Perlmy @pascalTriangle=( [1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1] );
Ссылки на анонимные ассоциативные массивы получаются путём перечисления пар «ключ — значение» через запятую внутри фигурных скобок:
Perl$refVoices={ 'собака'=>'гав', 'кошка'=>'мяу', 'корова'=>'муу', 'лошадь'=>'и-го-го', 'баран'=>'бее', 'свинья'=>'хрю' }; ссылка на ассоциативный массив
Ссылки на анонимные процедуры создаются командой sub
,
после которой имя процедуры можно не указывать:
Perl$refFactorial=sub { my $n=shift; my $factorial=1; for(my $i=2; $i<=$n; $i++) { $factorial*=$i; } return $factorial; };
Обратите внимание на точку с запятой после тела процедуры: без неё присваивание не будет завершено, что, скорее всего, приведёт к ошибке.
Примечание | |
---|---|
Конечно, таким способом нельзя создавать ссылки на рекурсивные процедуры, которые вызывают сами себя по имени. |
Для получения значения, на которое указывает ссылка, служат операторы разадресования ${…}, @{…}, %{…} и &{…}. Как вы можете догадаться, эти операторы применяются соответственно к ссылкам на скаляры, массивы, ассоциативные массивы и процедуры.
Perl$scalar=${$refScalar}; @array=@{$refArray}; %hash=%{$refHash};
В этом примере отсутствует разадресование ссылки на процедуру, поскольку процедуры в Perl — это не те объекты, которые можно присваивать.
В рассмотренном примере ссылки, к которым применялось разадресование, содержались в переменных. В этом случае допускается упрощённый синтаксис (без фигурных скобок):
Perl$scalar=$$refScalar; @array=@$refArray; %hash=%$refHash;
В случае, если ссылка, подлежащая разадресованию, является вычисляемым выражением (часто это вызов процедуры, которая возвращает ссылку), фигурные скобки необходимы:
Perl%hash=%{procedureThatReturnsReferenceToHash()}; @thirdLineOfPascalTriangle=@{$pascalTriangle[2]}; присваивается массив(1, 2, 1)
Чтобы получить элемент массива с заданным индексом по ссылке на этот массив, не обязательно разадресовывать массив:
Perl$sixthPrimeNumber=@$refPrimes[5]; присваивается13
Удобнее получить тот же результат с помощью оператора ->:
Perl$sixthPrimeNumber=$refPrimes->[5];
Похожим образом можно получить значение в ассоциативном массиве по заданному ключу по ссылке на ассоциативный массив:
Perl$catsVoice=$refVoices->{'кошка'}; присваивается'мяу'
Проницательные люди уже догадались, как можно вызвать процедуру по ссылке на неё:
Perl$sevenFactorial=&$refFactorial(7); $sevenFactorial=$refFactorial->(7); то же самое; присваивается5040
Если процедуру нужно вызвать без параметров, всё равно следует указать круглые
скобки: $refProc->()
.
Важно понимать разницу между копированием переменных и копированием ссылок на них. В первом случае копия переменной начинает жить своей собственной жизнью, а изменения копии никак не сказываются на оригинале (и наоборот):
Perl@primes=(2, 3, 5, 7, 11, 13, 17, 19, 23); $refPrimes=\@primes; $refPrimesCopy=$refPrimes; push @$refPrimes, 29; теперь в массив@primes
добавилось число29
push @$refPrimesCopy, 31; теперь в массив@primes
добавилось число31
Для проверки идентичности двух ссылок следует использовать арифметическое
сравнение: $refPrimes==$refPrimesCopy
. Идентичные
ссылки, конечно, указывают на один и тот же объект. Различные ссылки могут
случайно указывать на одинаковые объекты, но при этом не связанные друг
с другом.
Иногда требуется определить вид ссылки, точнее, того объекта, на который она указывает. К примеру, в зависимости от вида ссылки к ней могут применяться различные операторы разадресования (${…}, @{…}, %{…} и &{…}).
Попробуем вывести на печать содержимое ссылок:
Perlprint "$refScalar\n"; print "$refArray\n"; print "$refHash\n"; print "$refProc\n";
Тогда увидим следующее (числа в скобках наверняка будут другими):
SCALAR(0x83f56a8)
ARRAY(0x9986818)
HASH(0x969a818)
CODE(0x96da6a8)
Команда print
, получив ссылку, использует её строковое
представление, которое и выводится на печать. Перед скобками стоит слово,
по которому можно определить вид ссылки, а в скобках — шестнадцатеричная запись
адреса, по которому в памяти располагается адресуемый объект.
Адреса в памяти вряд ли будут полезны хоть для чего-нибудь, тем более они могут стать другими при следующем запуске той же программы. Программист никак не может повлиять на назначение этих адресов.
Для определения вида ссылки в Perl имеется процедура ref
,
которая принимает в качестве параметра ссылку, и возвращает одну из строк
SCALAR
, ARRAY
, HASH
,
CODE
, или ещё какое-нибудь обозначение, если ссылка не
относится ни к одному из перечисленных видов. Если аргумент процедуры
ref
не является ссылкой, возвращается пустая строка.