Ссылки

Часто при программировании на Perl бывает необходимо использовать структуры данных более сложные, нежели скалярные переменные, массивы или ассоциативные массивы. Простейший пример таких структур — двумерная таблица, элементы которой являются скалярными значениями. Её элементы расположены по строкам и столбцам. Такие таблицы называют ещё двумерными массивами, поскольку каждый их элемент задаётся двумя индексами — номерами строки и столбца.

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

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

@pascalTriangle=(
	(1),
	(1, 1),
	(1, 2, 1),
	(1, 3, 3, 1),
	(1, 4, 6, 4, 1),
	(1, 5, 10, 10, 5, 1)
);

Результат будет тем же самым, как если бы все элементы были бы записаны подряд, а не разделены по строкам таблицы:

@pascalTriangle=(1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, 1, 5, 10, 10, 5, 1);

Произойдёт то, что называют линеаризацией (выстраивание элементов в линию), так что внутренние круглые скобки не произведут желаемого эффекта. Поэтому мы лишаемся удобного способа извлечь из треугольника Паскаля биномиальный коэффициент C n k (число сочетаний из n по k) по номеру строки n и столбца k. Треугольнику Паскаля у нас посвящена целая глава.

Хотя массив не может содержать другие массивы, он может содержать ссылки на массивы.

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

Сильно забегая вперёд, скажем, что ссылки на различные структуры данных служат для представления объектов классов при объектно-ориентированном программировании (см. главу 38. «Объектно-ориентированное программирование»).

Ссылки в Perl могут быть созданы несколькими способами.

Ссылки на существующие переменные или процедуры создаются с помощью оператора адресования (\):

$refScalar=\$scalar;	в переменной $refScalar окажется ссылка
						на скалярную переменную $scalar
$refArray=\@array;		в переменной $refArray окажется ссылка на массив @array
$refHash=\%hash;		в переменной $refHash окажется ссылка
						на ассоциативный массив %hash
$refProc=\&proc;		в переменной $refProc окажется ссылка
						на процедуру proc

Тот же оператор \ позволяет создать ссылки на значения выражений:

$refString=\'школа';	ссылка на строку 'школа'
$refNumber=\(2*27);		ссылка на значение 54

Не обязательно создавать переменную-массив или ассоциативный массив, чтобы создать ссылку на неё. Имеется возможность создавать ссылки на анонимные (безымянные) переменные.

Выражение, состоящее из списка значений, перечисленных через запятую в квадратных скобках, даёт ссылку на анонимный массив:

$refPrimes=[2, 3, 5, 7, 11, 13, 17, 19, 23];	ссылка на массив —
												список нескольких простых чисел

Правильный способ создать структуру данных для хранения биномиальных коэффициентов в треугольнике Паскаля, будет таким:

my @pascalTriangle=(
	[1],
	[1, 1],
	[1, 2, 1],
	[1, 3, 3, 1],
	[1, 4, 6, 4, 1],
	[1, 5, 10, 10, 5, 1]
);

Ссылки на анонимные ассоциативные массивы получаются путём перечисления пар «ключ — значение» через запятую внутри фигурных скобок:

$refVoices={
	'собака'=>'гав',
	'кошка'=>'мяу',
	'корова'=>'муу',
	'лошадь'=>'и-го-го',
	'баран'=>'бее',
	'свинья'=>'хрю'
};	ссылка на ассоциативный массив

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

$refFactorial=sub
{
	my $n=shift;
	my $factorial=1;
	for(my $i=2; $i<=$n; $i++)
	{
		$factorial*=$i;
	}
	return $factorial;
};

Обратите внимание на точку с запятой после тела процедуры: без неё присваивание не будет завершено, что, скорее всего, приведёт к ошибке.

[Примечание]Примечание

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

Для получения значения, на которое указывает ссылка, служат операторы разадресования ${…}, @{…}, %{…} и &{…}. Как вы можете догадаться, эти операторы применяются соответственно к ссылкам на скаляры, массивы, ассоциативные массивы и процедуры.

$scalar=${$refScalar};
@array=@{$refArray};
%hash=%{$refHash};

В этом примере отсутствует разадресование ссылки на процедуру, поскольку процедуры в Perl — это не те объекты, которые можно присваивать.

В рассмотренном примере ссылки, к которым применялось разадресование, содержались в переменных. В этом случае допускается упрощённый синтаксис (без фигурных скобок):

$scalar=$$refScalar;
@array=@$refArray;
%hash=%$refHash;

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

%hash=%{procedureThatReturnsReferenceToHash()};
@thirdLineOfPascalTriangle=@{$pascalTriangle[2]};	присваивается массив (1, 2, 1)

Чтобы получить элемент массива с заданным индексом по ссылке на этот массив, не обязательно разадресовывать массив:

$sixthPrimeNumber=@$refPrimes[5];	присваивается 13

Удобнее получить тот же результат с помощью оператора ->:

$sixthPrimeNumber=$refPrimes->[5];

Похожим образом можно получить значение в ассоциативном массиве по заданному ключу по ссылке на ассоциативный массив:

$catsVoice=$refVoices->{'кошка'};	присваивается 'мяу'

Проницательные люди уже догадались, как можно вызвать процедуру по ссылке на неё:

$sevenFactorial=&$refFactorial(7);
$sevenFactorial=$refFactorial->(7);	то же самое; присваивается 5040

Если процедуру нужно вызвать без параметров, всё равно следует указать круглые скобки: $refProc->().

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

@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. Идентичные ссылки, конечно, указывают на один и тот же объект. Различные ссылки могут случайно указывать на одинаковые объекты, но при этом не связанные друг с другом.

Иногда требуется определить вид ссылки, точнее, того объекта, на который она указывает. К примеру, в зависимости от вида ссылки к ней могут применяться различные операторы разадресования (${…}, @{…}, %{…} и &{…}).

Попробуем вывести на печать содержимое ссылок:

print "$refScalar\n";
print "$refArray\n";
print "$refHash\n";
print "$refProc\n";

Тогда увидим следующее (числа в скобках наверняка будут другими):

Команда print, получив ссылку, использует её строковое представление, которое и выводится на печать. Перед скобками стоит слово, по которому можно определить вид ссылки, а в скобках — шестнадцатеричная запись адреса, по которому в памяти располагается адресуемый объект.

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

Для определения вида ссылки в Perl имеется процедура ref, которая принимает в качестве параметра ссылку, и возвращает одну из строк SCALAR, ARRAY, HASH, CODE, или ещё какое-нибудь обозначение, если ссылка не относится ни к одному из перечисленных видов. Если аргумент процедуры ref не является ссылкой, возвращается пустая строка.

Информатика-54© А. Н. Швец