The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Ссылочные структуры в Perl (perl array hash)


<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>
Ключевые слова: perl, array, hash,  (найти похожие документы)
From: Иван Рак (rak@tut.by) Newsgroups: http://rak.at.tut.by Date: Mon, 20 Sep 2004 18:21:07 +0000 (UTC) Subject: Ссылочные структуры в Perl Оригинал: http://rak.at.tut.by/perlreftut_ru.html perlreftut - очень короткое руководство по ссылкам, которое написал Марк * НАЗВАНИЕ * ОПИСАНИЕ * Зачем нужны сложные структуры данных? * Решение * Синтаксис * Создание ссылок * Правило создания 1 * Использование ссылокs * Правило использования 1 * Правило использования 2 * Пример * Правило стрелки * Решение * Остальное * Итого * Авторы * Условия распространения НАЗВАНИЕ perlreftut - очень короткое руководство по ссылкам, которое написал Марк ОПИСАНИЕ Одно из самых важных новшеств в Perl 5 - это возможность управления сложными структурами данных, такими, как многомерные массивы и вложенные хэши. Для этого в Perl 5 введены ссылки, и их использование позволяет работать с структурированной информацией. Однако, к сожалению, для этого приходится изучать довольно сложный синтакс, и основному руководству по ссылкам может быть трудно следовать. Руководство дает полное описание, и для некоторых людей это проблема, потому что трудно бывает отличить важное от второстепенного. К счастью, вам нужно знать только 10% того, что дает руководство, чтобы иметь 90% возможностей. На этой странице даны именно важные 10%. Зачем нужны сложные структуры данных? В Perl 4 постоянно вставала проблема представления данных в виде хэша, элементами которого являются списки. Конечно, в Perl 4 были хэши, но его значения должны были быть скалярами, и не могли быть списками. Для чего может понадобиться хэш списков? Возьмем простой пример: у вас есть файл с названиями городов и стран: Chicago, USA Frankfurt, Germany Berlin, Germany Washington, USA Helsinki, Finland New York, USA и вы хотите получить на выходе следующее: название страны, затем в алфавитном порядке список городов в этой стране: Finland: Helsinki. Germany: Berlin, Frankfurt. USA: Chicago, New York, Washington. Естественный путь сделать это - создать хэш, ключи которого - названия стран. Значением каждого ключа будет список городов этой страны. При чтении каждой входящей строки надо разделить ее на страну и город, найти список уже известных городов этой страны, и добавить город в список. После завершения чтения пройти по хэшу, [как обычно], сортируя каждый список городов перед печатью. Если значения хэша не могут быть списками, вы проиграли. В Perl 4 значения хэша не могут быть списками, они могут быть только строками. Вы проиграли. Возможно, вам придется собирать все города в одну строку, перед печатью разбивать строку в список, сортировать список, и превращать его обратно в строку. Это добавит беспорядка и ошибок. И это утомительно [напряжно. frustrating], потому что в Perl'е уже есть замечательные списки, которые могут решить проблему... если вы сможете их использовать. Решение Ко времени обкатки Perl 5 мы уже смирились с тем, что что значения хэша должны быть скалярами. Решение пришло в виде ссылок. Ссылка - это скалярная величина, которая ссылается на весь массив или на весь хэш (или еще на что-нибудь). Например, имена - уже известный вам вид ссылок. Подумайте о президенте США: дурацкий, неудобный мешок с кровью и и костями. Но чтобы говорить о нем, или представить его в компьютерной программе, нужна простая удобная строка ``Джордж Буш''. Ссылки в Perl похожи на имена для списков или хэшей. Это частные, внутренние имена, поэтому вы можете быть уверены, что они однозначны. В отличие от слов ``Джордж Буш', ссылка ссылается только на один объект, и вы всегда знаете, на какой. Если у вас есть ссылка на массив, вы можете получить весь массив. Если у вас есть сслыка на хэш, вы можете получить весь хэш. Но ссылка все еще легкая, компактная скалярная величина. Не может быть хэша, значения которого - массивы; значения хэша могут быть только скалярами. Мы опять застряли. Но одна ссылка может указывать на целый массив, и ссылки - скаляры, так что можно создать хэш ссылок на массивы, и он будет работать, как хэш массивов, и будет таким же полезным, как хэш массивов. Мы вернемся к проблеме городов и стран позже, после того, как мы рассмотрим синтаксис управления ссылками Синтаксис Существует два способа создания ссылок, и два способа их использования. Создание ссылок Правило создания 1 Если поместить перед переменной \, вы получите ссылку на эту переменную. $aref = \@array; # $aref содержит ссылку на @array $href = \%hash; # $href содержит ссылку на %hash После того, как ссылка сохранена в переменной, ее можно копировать или просто хранить, как любую другую скалярную величину: $xy = $aref; # $xy теперь содержит ссылку на @array $p[3] = $href; # $p[3] содержит ссылку на %hash $z = $p[3]; # $z содержит ссылку на %hash В этих примерах показано, как создавать ссылки на именованные объекты. Может быть, вы захотите создать массив или хэш, у которого нет имени, по аналогии с возможностью использовать строку "\n" или число 80, не сохраняя их в именованной переменной. Правило создания 2 [ ЭЛЕМЕНТЫ ] создает новый анонимный массив, и возвращает ссылку на этот массив. { ЭЛЕМЕНТЫ } }создает новый анонимный хэш, и возвращает ссылку на этот хэш. $aref = [ 1, "foo", undef, 13 ]; # $aref содержит ссылку на массив $href = { APR => 4, AUG => 8 }; # $href содержит ссылку на хэш Ссылка, которую вы получаете по правилу 2, такая же, как и та, которая создана по правилу 1: # Это: $aref = [ 1, 2, 3 ]; # Делает то же, что и это: @array = (1, 2, 3); $aref = \@array; Первая строка - сокращение последующих двух строк, кроме того, что она не создает излишней переменной массива @array Если вы напишете просто [], вы получите новый пустой анонимный массив. Если вы напишите просто {}, вы получите новый пустой анонимный хэш. Использование ссылок Что можно сделать с созданной переменной? Мы уже видели, что ее можно хранить, как скалярную величину и получить ее значение. Есть еще два способа ее использования. Правило использования 1 Можно использовать имя сслыки на массив в фигурных скобках вместо имени массива. Например, @{$aref} вместо @array. Несколько примеров: Массивы: @a @{$aref} Массив reverse @a reverse @{$aref} Массив в обратном порядке $a[3] ${$aref}[3] Элемент массива $a[3] = 17; ${$aref}[3] = 17 Присваивание значения элементу массива В каждой строке два выражения, которые делают то же самое. Слева варианты, которые работают с массивом @a. Справа - с массивом, на который сслылаетс я$aref. После нахождения массива оба варианта выполняют одинаковые операции. Ссылки на хэш используются точно так же: %h %{$href} Хэш keys %h keys %{$href} Получение ключей хэша $h{'red'} ${$href}{'red'} Элемент хэша $h{'red'} = 17 ${$href}{'red'} = 17 Присвоение значения элементу Все, что можно сделать с ссылкой, делается по Правилу использования 1. Вы просто пишете код на Perl, который делает то, что нужно с обычным массивом или хэшем, а затем заменяете имя массива или хэша {$ссылкой}. ``Как перебрать элементы массива, если у меня есть ссылка?'' Чтобы перебрать элементы массива, нужно написать for my $element (@array) { ... } затем замените имя массива @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"; } Правило использования 2 Правило использования 1 - это все, что вам нужно, потому что оно описывает абсолютно все действия с ссылкой. Но чаще всего приходится извлекать единственный элемент массива или хэша, и запись по Правилу использования 1 громоздка. Поэтому - несколько сокращений. ${$aref}[3] трудно читать, поэтому можно писать $aref->[3]. ${$href}{red} трудно читать, поэтому можно писать $href->{red}. Если $aref содержит ссылку на массив, то $aref->[3] - четвертый элемент массива. Не перепутайте его с $aref[3], что есть четвертый элемент совершенно другого массива, обманчиво названного @aref. $aref and @aref are unrelated the same way that $item and @item are. Так же, $href->{'red'} - часть хэша, на который указывает скалярная переменная $href, возможно даже безымянного. $href{'red'} часть обманчивого названного %href хэша. Легко забыть вставить ->, и в таком случае вы получите странные результаты, когда программа будет извлекать элементы из совершенно неожиданные хэшей и массивов, которые вы совсем не хотели использовать. Пример Небольшой пример того, как все это можно использовать. Во-первых, вспомним, что [1, 2, 3] создает анонимный массив, содержащий (1, 2, 3), и возвращает ссылку на этот массив. Тогда: @a = ( [1, 2, 3], [4, 5, 6], [7, 8, 9] ); @a - массив из трех элементов, каждый из которых есть ссылка на другой массив. $a[1] одна из таких ссылок. Она ссылается на массив, содержащий (4, 5, 6), и, так как это ссылка на массив, по Правилу использования 2 мы можем написать $a[1]->[2], чтобы получить третий элемени этого массива. $a[1]->[2] равно 6. $a[0]->[1] равно 2. То, что мы имеем, похоже на двумерный массив; можно писать $a[СТРОКА]->[СТОЛБЕЦ], чтобы получть элемент в любой строке и любом столбце массива. Но эта нотация все еще тяжеловата, поэтому есть еще одно сокращение: Правило стрелок Между квадратными скобками стрелка не обязательна. Вместо $a[1]->[2] мы можем написать $a[1][2]; результат тот же. Вместо$a[0]->[1] = 23, мы можем написать $a[0][1] = 23; результат тот же. Теперь это в самом деле выглядит, как двумерный массив! Из этого видно, почему важны стрелки. Без них пришлось бы писать ${$a[1]}[2] вместо $a[1][2]. Для трехмерных массивов можено писать $x[2][3][5]] вместо нечитаемого ${${$x[2]}[3]}[5]. Решение Теперь - решение поставленнного в начале вопроса о переформатировании названий городов и стран. 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 анализируют данные и распечатывают отчет. Мы получаем хэш %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 } %table - обычный хэш, и мы получаем список его ключей, сортируем ключи, и перебираем ключи как обычно. Единственное использование ссылок - в строке 10. $table{$country} ищет ключ $country в хэше, и получает значение - ссылку на массив городов этой страны. Правило использования 1 говорит, что мы можем получить массив, написав @{$table{$country}}. Строка 10 похожа на @cities = @array; кроме того, что имя array заменено ссылкой {$table{$country}}. Знак @ позволяет 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 проверяет наличие такой страны в ключах хэша. Если ее нет, программа использует запись [] (Правило создания 2), чтобы получить новый пустой анонимный массив городов, и установить ссылку на него в качестве значения для соответствующего ключа хэша. Строка 6 добавляет название города в соответствующий массив. $table{$country} push @array, $city; кроме того, что имя array заменено ссылкой {$table{$country}}. Команда push [TODO: link] добавляет название города в конец указываемого массива. Есть один тонкий момент, который я пропустил. Строка 5 необязательна, поэтому ее можно выбросить 2 while (<>) { 3 chomp; 4 my ($city, $country) = split /, /; 5 #### $table{$country} = [] unless exists $table{$country}; 6 push @{$table{$country}}, $city; 7 } Если в %table уже есть запись для текущей страны $country, тогда ничего не изменится. Строка 6 найдет значение $table{$country}, которое указывает на массив, и добавит город $city в массив. Но что произойдет, если $country содержит ключ, которого еще нет в %table, например, Greece? Это Perl, поэтому все пройдет так, как надо. Если вы хотите добавить Athens в несуществующий массив, то будет правильно [TODO:] создать новый пустой анонимный массив, включить его в %table, и добавить в него Athens. Это называется 'autovivification' [TODO: автооживление] -- автоматическое оживление вещей. Perl видит, что такого ключа не существует в хэше, так что он создает новый хэш автоматически. Perl видит, что вы хотите использовать значение ключа как массив, поэтому он создает новый пустой массив, и автоматически включает сслыку на него в хэш. И как обычно, Perl делает массив на один элемент длиннее, чтобы сохранить в нем новое название города. Остальное Я обещал дать 90% возможностей, используя 10% подробностей, и это значит, что 90% подробностей я упустил. Теперь, после обзора самого важного, вам будет легче читать the perlref manpage[TODO: link], в котором даны 100% информации. Несколько важных мест the perlref manpage [TODO: link]: * Можно создавать ссылки на все объекты, включая скаляры, функции, и другие ссылки. * По Правилу использования 1 вы можете опустить фигурные скобки всегда, the thing inside them is an atomic scalar variable like $aref. [TODO:] Например, @$aref - то же, что @{$aref}, а $$aref[1] - то же, что ${$aref}[1]. Пока вы не освоитесь, вам лучше привыкнуть всегда писать фигурные скобки. * Так массив не скопировать: $aref2 = $aref1; Вы получите два указателя на один и тот же массив. Если вы измените $aref1->[23], а затем посмотрите на $aref2->[23], вы увидите изменения. Чтобы скопировать массив, используйте $aref2 = [@{$aref1}]; Здесь используется запись [...] для того, чтобы создать новый анонимный массив, и $aref2 получит значение ссылки на новый массив. Новый массив инициализируется содержимым массива, на который указывает $aref1. Похожим способом можно скопировать анонимный хэш: $href2 = {%{$href1}}; * Чтобы узнать, содержит ли переменная ссылку, используйте функцию ref [TODO: link]. Она возвращает истину, если аргумент есть ссылка. На самом деле, даже лучше: она возвращает code>HASH для ссылок на хэш, и ARRAY для ссылок на массив. * Если вы попробуете использовать ссылку как строку, вы получите строки вроде таких: ARRAY(0x80f5dec) or HASH(0x826afc0) Если вы увидите такие строки - знайте: вы по ошибке распечатали ссылку. Побочный эффект такого представления в том, что вы можете использовать eq для проверки того, на один ли и тот же объект указывают ссылки. (Но обычно лучше использовать ==, потому что так работает намного быстрее). * Вы можете использовать строку, как если бы это была ссылки. Если использовать строку "foo" как ссылку на массив, это будет считаться ссылкой на массив @foo. Это так называемая мягкая ссылка, или символическая ссылка [TODO:]. Объявление use strict 'refs' отключает эту возможность, которая может вызвать массу проблем, если используется по ошибке. Возможно, вместо the perlref manpage [TODO: link], вам стоит сначала прочесть the perllol manpage [TODO:], в котором подробно обсуждаются списки списков и многомерные массивы. После этого ознакомьтесь с the perldsc manpage [TODO:link] - Поваренной Книгой Структур Данных (Data Structure Cookbook), в которой даны рецепты использования и распечатки массивов хэшей, хэшей массивов, и тому подобного. Итого Всем нужны сложные структуры данных, и Perl позволяет создавать их с помощью ссылок. Есть четыре правила управления ссылками: два правила создания и два правила использования. Когда вы усвоите эти правила, вы сможете делать почти все важное, что можно сделать с ссылками. Авторы. Автор: Mark Jason Dominus, Plover Systems (mjd-perl-ref+@plover.com) Впервые статья появилась в The Perl Journal (http://www.tpj.com/), том 3, #2. Перепечатывается с разрешения [это про оригинал] Первоначально статья называлась ``Пойми ссылки сегодня'' (Understand References Today). Перевод: Иван Рак (rak@tut.by) Условия распространения Copyright 1998 The Perl Journal. Документ бесплатный; вы можете распространять и/или изменять его на тех же условиях, что и сам Perl. Независимо от распространения, все примеры кода в этом файле являются публичным достоянием. Разрешается и поощряется использование этого кода в ваших собственных программах для развлечения или для получения прибыли. Простой комментарий с указанием авторства приятен, но не обязателен. perlreftut - очень короткое руководство по ссылкам, которое написал Марк

<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>

Обсуждение [ RSS ]
  • 1.1, Дмитрий (??), 20:12, 07/04/2005 [ответить]  
  • +/
    Отличная статья! Спасибо большое авторам и переводчику! Успешно использовал код с hash table.
     
  • 1.2, Алексей (??), 12:46, 04/01/2006 [ответить]  
  • +/
    Никак не мог найти каким образом сделать for имея ссылку на массив, спасибо!
     
  • 1.3, KES (?), 02:53, 09/08/2006 [ответить]  
  • +/
    Супер!!! Распечатал, положил на полку!
     
  • 1.4, DJAndreysXe (?), 16:37, 24/08/2006 [ответить]  
  • +/
    Отличная статья, спасибо. Рекомендую.
     
  • 1.6, Fedor (??), 13:59, 20/04/2007 [ответить]  
  • +/
    Отличная статья!
     
  • 1.7, Alex (??), 06:38, 07/02/2011 [ответить]  
  • +/
    Премного благодарен за столь доходчивый перевод.
     
  • 1.8, vt (??), 20:10, 11/07/2011 [ответить]  
  • +/
    отличная статья, отличный перевод.
    спасибо!
     

    игнорирование участников | лог модерирования

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Спонсоры:
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2020 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру