
=encoding utf8

=head1 NAME/НАИМЕНОВАНИЕ

perlreftut - Краткое руководство по ссылкам, которое написал Марк

=head1 ОПИСАНИЕ

Одним из наиболее важных новых функций в Perl 5 была возможность
управления сложными структурами данных такими, как многомерные массивы и
вложенные хэши. Чтобы сделать это в Perl 5 появилась возможность
 называемая 'ссылки' ('references') и использование ссылок является ключом к управлению сложными,
структурированными данными в Perl. К сожалению есть много смешного синтаксиса
для изучения, и главная страница маны (perlref) может быть сложна для изучения. Это Руководство - 
 довольно полное, и иногда люди находят и здесь проблемы, потому что
 иногда бывает трудно сказать, что важно, а что нет.

К счастью вам только нужно знать только 10% того, что находится на главной странице (perlref), чтобы получить 
90% выгоды. Эта страница покажет вам, что это за 10%.

=head1 Кому нужны сложные структуры данных?

Все время вставала проблема представления данных в виде хэша, значениями которого являются списки. 
В Perl есть хэши, конечно, но значениями хэша должны быть скаляры;
они не могут быть списками.

Для чего может понадобиться хэш списков?
Возьмем простой пример: у вас есть файл с названиями городов и стран:

	Chicago, USA
	Frankfurt, Germany
	Berlin, Germany
	Washington, USA
	Helsinki, Finland
	New York, USA

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

	Finland: Helsinki.
	Germany: Berlin, Frankfurt.
	USA:  Chicago, New York, Washington.

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

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

=head1 Решение

Ко времени обкатки Perl 5 мы уже завязли в этой концепции: 
Значениями хэша могут быть только скаляры. Решение пришло в виде ссылок.

Ссылка — это скалярная величина, которая I<ссылается> на весь массив или на весь хэш (или еще на что-нибудь). Имена — один из видов ссылок, которые вам уже известны. Подумайте о президенте США: грязный, неудобный мешок с кровью и костями. Но чтобы говорить о нем, или представить его в компьютерной программе, все что для этого нужно - удобная скалярная строка "Барак Обама".

Ссылки в Perl похожи на имена для списков или хэшей. Это частные, внутренние имена perl программы, поэтому можно быть уверенным, что они однозначны. В отличие от "Барака Обама", ссылка ссылается только на один объект, и вы всегда знаете, на какой. Если у вас есть ссылка
в массив, то весь массив можно восстановить из нее. Если у вас есть
ссылка на хэш, то вы можете восстановить весь хэш. 
Но ссылка по-прежнему является простым, компактным скалярным значением.

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

Мы вернемся к проблеме городов и стран позже, после того, как рассмотрим синтаксис управления ссылками.

=head1 Синтаксис

Есть только два способа создать ссылку и только два способа использовать её.

=head2 Создание ссылок

=head3 B<Правило создания 1>

Поместив перед переменной обратный слеш C<\>, вы получите ссылку на эту переменную.

    $aref = \@array;         # $aref теперь содержит ссылку на @array
    $href = \%hash;          # $href теперь содержит ссылку на %hash
    $sref = \$scalar;        # $sref теперь содержит ссылку на $scalar

После того, как ссылка сохранена в переменной, такой как $aref или $href,
ее можно копировать или просто хранить, как любую другую скалярную величину:

    $xy = $aref;             # $xy теперь содержит ссылку на to @array
    $p[3] = $href;           # $p[3] теперь содержит ссылку на %hash
    $z = $p[3];              # $z теперь содержит ссылку на %hash

Эти примеры показывают, как сделать ссылки на именованные переменные.
Может быть, вы захотите создать массив или хэш, у которого нет имени,
по аналогии с возможностью использовать строку C<"\n"> или число 80, 
не сохраняя их в именованной переменной.

B<Правило создания 2>

Конструкция [ элемент1, элемент2, ... ] создает новый анонимный 
массив и возвращает ссылку на этот массив. Конструкция { элемент1 => элемент2, ... } 
создает новый анонимный хэш и возвращает ссылку на этот хэш.

C<[ ЭЛЕМЕНТЫ ]> создает новый анонимный массив и возвращает ссылку на этот массив.  

C<{ ЭЛЕМЕНТЫ }> создает новый анонимный хэш и возвращает ссылку на этот хэш.  

    $aref = [ 1, "foo", undef, 13 ];
    # $aref теперь содержит ссылку на массив

    $href = { APR => 4, AUG => 8 };
    # $href теперь содержит ссылку на хэш

Ссылки, которые вы получаете по правилу 2, такие же, как и те, которые получаются по правилу 1:

	# Это:
	$aref = [ 1, 2, 3 ];

	# Делает то же самое, что и это:
	@array = (1, 2, 3);
	$aref = \@array;

Первая строка — сокращение последующих двух строк, кроме того, 
что она не создает лишней переменной массива C<@array>.

Если вы напишете просто C<[]>, вы получите новый пустой анонимный массив. 
Если вы напишите просто C<{}>, вы получите новый пустой анонимный хэш.

=head2 Использование ссылок

Что можно сделать со ссылкой после её создания ? 
Мы уже видели, что её можно хранить как скалярную величину и 
получить её значение. Есть только два способа её использования:

=head3 B<Правило использования 1>

Можно использовать ссылку на массив в фигурных скобках,
вместо имени массива. Например, C<@{$aref}> вместо  C<@array>.

Вот некоторые примеры этого:

Массивы:

	@a		@{$aref}		Массив
	reverse @a	reverse @{$aref}	Массив в обратном порядке
	$a[3]		${$aref}[3]		Элемент массива
	$a[3] = 17;	${$aref}[3] = 17	Присваивание значения элементу массива

В каждой строке два выражения, которые делают то же самое.
В левой версии работают с массивом C<@a>. 
В правой версии работают с массивом, на который ссылается C<$aref>.
После нахождения массива оба варианта выполняют одинаковые операции.

Использование ссылки на хэш I<точно> такое же:

	%h		%{$href}	      Хэш
	keys %h		keys %{$href}	      Получение ключей хэша
	$h{'red'}	${$href}{'red'}	      Элемент хэша
	$h{'red'} = 17	${$href}{'red'} = 17  Присвоение значения элементу

Все что можно сделать со ссылкой, делается по B<Правилу использования 1>.
Вы просто пишите код на Perl, который делает то, что нужно с обычным
массивом или хэшем, а затем заменяете имя массива или хэша C<{$ссылкой}>.
"Как перебрать элементы массива, если у меня есть ссылка ?" 
Чтобы перебрать элементы массива, нужно написать


        for my $element (@array) {
           ...
        }

затем замените имя массива C<@array> ссылкой:

        for my $element (@{$aref}) {
           ...
        }

"Как распечатать содержимое хэша, если у меня есть только ссылка?"
Сначала напишем код для распечатки хэша:

        for my $key (keys %hash) {
          print "$key => $hash{$key}\n";
        }

И затем заменяем имя хэша ссылкой:

        for my $key (keys %{$href}) {
          print "$key => ${$href}{$key}\n";
        }

=head3 B<Правило использования 2>

B<Правило использования 1> — это все, что вам действительно нужно,
потому что оно описывает абсолютно все действия со ссылкой.
Но чаще всего приходится извлекать единственный элемент массива
или хэша, и запись по B<Правилу использования 1> громоздка.
Поэтому есть несколько сокращений.

C<${$aref}[3]>  слишком трудно читать, так что вы можете писать $aref->[3]
вместо этого.

C<${$href}{red}> слишком трудно читать, так что вы можете написать
C<< $href->{red} >> вместо этого.

Если C<$aref> содержит ссылку на массив, тогда C<< $aref->[3] >> — 
четвертый элемент массива. Не перепутайте его с  C<$aref[3]>, 
что есть четвертый элемент совершенно другого массива,
обманчиво названного C<@aref>.
 C<$aref> и C<@aref> не связаны также, как и C<$item> и C<@item> .

Аналогичным образом C<< $href->{'red'} >> — является частью хэша, на который указывает скалярная переменная $href,
возможно даже безымянного.
C<$href{'red'}> часть обманчиво названного хэша C<%href>. 
Легко забыть вставить  C<< -> >> и в этом случае вы получите странные результаты, когда программа будет извлекать элементы совершенно неожиданных массивов и хэшей, которые вы совсем не хотели использовать.

=head2 Пример

Небольшой пример того, как все это можно использовать.

Во-первых, следует помнить, что C<[1, 2, 3]> создает анонимный массив, 
содержащий C<(1, 2, 3)>, и возвращает ссылку на этот массив.

Теперь подумайте о

	@a = ( [1, 2, 3],
               [4, 5, 6],
	       [7, 8, 9]
             );

@a представляет собой массив из трех элементов, каждый из которых является ссылкой на другой массив.

C<$a[1]> одна из этих ссылок. Она ссылается на массив, содержащий
(4, 5, 6), и, так как это ссылка на массив, то по B<Правилу использования 2>
мы можем написать C<< $a[1]->[2] >>, чтобы получить третий элемент этого массива.
C<< $a[1]->[2] >> это 6. Аналогично, C<< $a[0]->[1] >> это 2. То, что у нас есть
похоже на двумерный массив, можно писать  C<< $a[СТРОКА]->[КОЛОНКА] >>, чтобы
получить или внести элемент в любую строку любого столбца массива.

Но эта нотация все еще громоздка, поэтому есть еще одно сокращение:

=head2 Правило стрелки

Между двумя B<индексами> стрелка не обязательна.

Вместо C<< $a[1]->[2] >>, мы можем написать C<$a[1][2]>; это значит тоже самое. 
Аналогично, вместо C<< $a[0]->[1] = 23 >>, мы можем написать C<$a[0][1] = 23>.

Теперь это в самом деле выглядит, как двумерный массив!

Из этого видно, почему важны стрелки. 
Без них пришлось бы писать  C<${$a[1]}[2]> вместо C<$a[1][2]>. 
Для трехмерных массивов можно писать C<$x[2][3][5]>
вместо нечитаемого C<${${$x[2]}[3]}[5]>.

=head1 Решение

Теперь — решение поставленнного в начале вопроса о переформатировании названий городов и стран.

    1   my %table;

    2   while (<>) {
    3    chomp;
    4     my ($city, $country) = split /, /;
    5     $table{$country} = [] unless exists $table{$country};
    6     push @{$table{$country}}, $city;
    7   }

    8   foreach $country (sort keys %table) {
    9     print "$country: ";
   10     my @cities = @{$table{$country}};
   11     print join ', ', sort @cities;
   12     print ".\n";
   13	}

В программе две части: Строки 2—7 считывают данные и создают 
структуры данных, а строки 8—13 анализируют данные и распечатывают отчет.
Мы получаем хэш  C<%table>, ключи которого — названия стран, а значения — 
ссылки на массивы названий городов. Данная
структура будет выглядеть следующим образом:


           %table
        +-------+---+
        |       |   |   +-----------+--------+
        |Germany| *---->| Frankfurt | Berlin |
        |       |   |   +-----------+--------+
        +-------+---+
        |       |   |   +----------+
        |Finland| *---->| Helsinki |
        |       |   |   +----------+
        +-------+---+
        |       |   |   +---------+------------+----------+
        |  USA  | *---->| Chicago | Washington | New York |
        |       |   |   +---------+------------+----------+
        +-------+---+

Рассмотрим сначала вывод. Предположим, что у нас уже есть эта структура, как ее распечатать?

    8   foreach $country (sort keys %table) {
    9     print "$country: ";
   10     my @cities = @{$table{$country}};
   11     print join ', ', sort @cities;
   12     print ".\n";
   13	}

C<%table> — обычный хэш, и мы получаем список его ключей, которые, 
как обычно, сортируем и перебираем. Единственное использование ссылки 
в строке 10. C<$table{$country}> ищет в хэше ключ  C<$country> и получает
значение — ссылку на массив городов этой страны. По B<Правилу использования 1>
мы можем получить массив обратно используя конструкцию C<@{$table{$country}}>.
Строка 10 равносильна такой конструкции:

	@cities = @array;

кроме того, что имя  C<array> заменено ссылкой C<{$table{$country}}>. 
Знак C<@> указывает интерпретатору Perl получить весь массив. 
Получив список городов, как обычно, сортируем его соединяем и распечатываем.

Строки 2-7 отвечают в первую очередь за построение структуры. Приводим их еще раз:

    2   while (<>) {
    3    chomp;
    4     my ($city, $country) = split /, /;
    5     $table{$country} = [] unless exists $table{$country};
    6     push @{$table{$country}}, $city;
    7   }

В строках 2-4 мы получаем название города и страны. Строка 5 
проверяет наличие такой страны в ключах хэша. Если ее нет, программа 
использует запись C<[]> (B<Правило создания 2>), чтобы получить новый пустой 
анонимный массив городов, и установить ссылку на него в качестве значения
для соответствующего ключа хэша.

Строка 6 добавляет название города в соответствующий массив. 
C<$table{$country}> теперь содержит ссылку на массив городов в этой стране, 
которые внесены до данного момента. Строка 6 подобна

	push @array, $city;

кроме того, что имя C<array> заменено ссылкой C<{$table{$country}}>.
C<push> добавляет название города в конец указываемого массива.

Есть один интересный момент, который я упустил.
Строка 5 не является необходимой, и мы можем избавиться от неё.

    2   while (<>) {
    3    chomp;
    4     my ($city, $country) = split /, /;
    5   ####  $table{$country} = [] unless exists $table{$country};
    6     push @{$table{$country}}, $city;
    7   }

Если в  C<%table> уже есть запись для текущей страны C<$country>, 
то ничего не изменится. Строка 6 найдет значение C<$table{$country}>, 
которое указывает на массив, и добавит город C<$city> в массив. 
Но что произойдет, если C<$country> содержит ключ, которого еще нет 
в  C<%table>, например, C<Greece>?

Это Perl, поэтому он сделает верно. Он видит, что вы хотите
положить C<Athens> в массив, который не существует, поэтому он услужливо
делает новый, пустой, анонимный массив для вас, устанавливает его в
C<%table>, а затем помещает C<Athens> в него. Это называется
'autovivification' — приведение вещей к жизни автоматически. Perl видит, 
что ключ не был в хэше, поэтому он создал новую запись хэша
автоматически. Perl увидел, что вы хотели использовать хэш-значение как
массив, поэтому он создал новый пустой массив и установил на него ссылку
в хэш автоматически. И как обычно, Perl сделал массив на один 
элемент больше, чтобы сохранить имя нового города.

=head1 Остальное

Я обещал дать 90% возможностей, используя 10% подробностей, 
и это значит, что 90% подробностей я упустил. Теперь, 
после обзора самого важного, вам будет легче читать perlref,
в котором даны 100% информации.

Некоторые из самых значимых моментов L<perlref>:

=over 4

=item *

Можно создавать ссылки на все объекты, включая скаляры, функции, и другие ссылки.

=item *

По B<правилу использования 1> можно опустить фигурные скобки, 
когда в них атомарная скалярная переменная такая как C<$aref>. Например,
C<@$aref> — то же самое, что и C<@{$aref}>, а C<$$aref[1]> — то же, что и C<${$aref}[1]>.
Пока вы не освоитесь, вам лучше привыкнуть всегда писать фигурные скобки.

=item *

Так массив не скопировать:

        $aref2 = $aref1;

Вы получаете две ссылки на один и тот же массив. 
Если вы измените C<< $aref1->[23] >> и затем посмотрите на C<< $aref2->[23] >>, вы увидите, что они не равны.

Чтобы скопировать массив, используйте

        $aref2 = [@{$aref1}];

Здесь используется запись C<[...]> для создания нового анонимного массива.
Переменной C<$aref2>  присваивается ссылка на новый массив. Новый массив 
инициализируется содержимым массива, на который указывает  C<$aref1>.

Похожим способом можно скопировать анонимный хэш:

        $href2 = {%{$href1}};

=item *

Чтобы узнать, содержит ли переменная ссылку, используйте функцию C<ref>. 
Она возвращает истину, если ее аргумент является ссылкой. На самом деле она 
делает даже лучше: возвращает C<HASH> для ссылки на хэш и C<ARRAY> для ссылки на массив.

=item *

Если попробуете использовать ссылку как строку, вы получите строки вроде таких

	ARRAY(0x80f5dec)   или    HASH(0x826afc0)

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

Побочный эффект такого представления в том, что вы можете использовать C<eq> для проверки 
того, на один ли и тот же объект указывают ссылки. (Но обычно лучше использовать C<==>,
потому что так работает намного быстрее).

=item *

Вы можете использовать строку, как если бы она была ссылкой. 
Если вы используете строку C<"foo"> как ссылку на массив, она считается как 
ссылка на массив C<@foo>. Это так называемая I<мягкая ссылка> или I<символическая ссылка>.
Объявление C<use strict 'refs'> отключает эту возможность, которая может вызвать
всевозможные неприятности, если вы используете её случайно.

=back

Возможно, вместо  L<perlref>, вам стоит сначала прочесть L<perllol>, 
в ней детально обсуждаются списки списков и многомерные массивы.
После этого ознакомьтесь с L<perldsc> — справочник структур данных, в котором даны 
рецепты по использованию и распечатки массива хэшей, хэшей массивов, и других типов данных.

=head1 Резюме

Всем нужны сложные структуры данных, и Perl позволяет создавать их с 
помощью ссылок. Есть четыре правила управления ссылками: два правила 
создания и два правила использования. Когда вы усвоите эти правила,
вы сможете делать почти все важное, что можно сделать со ссылками.

=head1  Похвала

Автор: Mark Jason Dominus, Plover Systems (C<mjd-perl-ref+@plover.com>)

Впервые статья появилась в I<The Perl Journal>
([1] http://www.tpj.com/ ) том 3, #2.  Перепечатывается с разрешения.

Первоначально статья называлась I<Пойми ссылки сегодня> (анг. I<Understand References Today>).

=head2 Условия распространения

Copyright 1998 The Perl Journal.

Этот документ распространяется свободно, и вы можете свободно распространять
и/или изменять его на тех же условиях, как и сам Perl.

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

=head1 ССЫЛКИ

[1] L<http://www.tpj.com/>

[2] L<http://perl5doc.ru/perlreftut>

=head1 ПЕРЕВОДЧИКИ
 
=over
 
=item * Николай Мишин C<< <mi@ya.ru> >>

=item * Динар Жамалиев C<< <admin@perl5doc.ru> >> [2]

=item * Иван Рак C<< <rak@tut.by> >>

=back

