ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
11.1. Ссылки на массивыПроблемаТребуется работать с массивом через ссылку.РешениеСсылка на массив создается следующим образом:$aref = \@аrrау; $anon_array = [1, 3, 5, 7, 9]; $anon_copy = [ @аrrау ]; @$implicit_creation = (2, 4, 6, 8, 10); Чтобы разыменовать ссылку на массив, поставьте перед ней символ @: push(@$anon_array, 11); Или воспользуйтесь стрелкой с указанием индекса конкретного элемента в квадратных скобках: $two = $implicit_creation->[0]; Для получения индекса последнего элемента массива по ссылке или определения количества элементов в массиве применяется следующая запись: $last_idx = $#$aref; $num_items = @$aref; Дополнительные фигурные скобки повышают надежность и форсируют ну/к ный контекст: $last_idx = $#{ $aref }; $num_items = scalar @{ $aref }; КомментарийРассмотрим примеры использования ссылок на массивы: # Проверить, содержит ли $someref ссылку на массивif (ref($someref) ne 'ARRAY') { die "Expected an array reference, not $someref\n"; } print "@{$array_ref}\n"; # Вывести исходные данные @order = sort @{ $array_ref }; # Отсортировать их push @{ $array_ref }, $item; # Добавить в массив новый элемент Если вы не можете выбрать между использованием ссылки на именованпыи массив и созданием нового массива, существует простое правило, которое в большинстве случаев оказывается верным. Получение ссылки на существующий массив используется либо для возврата ссылки за пределы области действия, либо при передаче массива функции по ссылке. Практически во всех остальных случаях используется [@аrrау], что приводит к созданию ссылки на новый массив с копиями старых значений. Автоматический подсчет ссылок в сочетании с оператором \ обладает большими возможностями: sub array_ref { my @array; return \@array: $aref1 = array_ref(); $aref2 = array_ref(); При каждом вызове array_ref функция выделяет для ©array новый блок памяти. Если бы мы не вернули ссылку на @аrrау, то занимаемая массивом память была бы возвращена при выходе из блока, то есть при завершении подпрограммы. Однако ссылка на @аrrау продолжает существовать, поэтому Perl не освобождает намять, и мы получаем ссылку на блок памяти, недоступный через таблицу символов. Такие блоки памяти называются анонимными, поскольку с ними не связано никакое имя. К определенному элементу массива, на который указывает ссылка $aref, можно обратиться в форме $$aref[4], но $aref->[4] делает то же самое и обладает большей наглядностью. print $array_ref->[$N], # Обращение к М-му элементу (лучший вариант) print $$array_ref[$N]; # To же, но менее наглядно print ${$array_ref}[$N]; # To же, но непонятно и уродливо Имея ссылку на массив, можно получить срез субъектного массива: (@$pie[3. .5]; # Срез массива, но читается плохо @{$pie}[3..5]; # Срез массива, читается лучше (?) Срезы массивов, даже при обращении через ссылки, допускают присваивание. В следующей строке сначала происходит разыменование массива, после чего элементам среза присваиваются значения: @{$pie}[3..5] = ("blackberry", "blueberry", "pumpkin"); Срез массива полностью идентичен списку отдельных элементов. Поскольку o сылку на список получить нельзя, вам не удастся получить ссылку на срез массива: $sliceref = \@{$pie}[3.,5]; # НЕВЕРНО! Для перебора в массиве применяется цикл foreach или for: foreach $item ( @{$array_ref} ) { # Данные в $item } for ($idx = 0; $idx <= $@{ $array_ref }; $idx++) {# Данные в $array_ref->[$idx] } > Смотри также
11.2. Создание хэшей массивовПроблемаС каждым ключом хэша может быть ассоциирована лишь одна скалярная величина, однако вам хочется использовать один ключ для хранения и извлечения нескольких величин. Иначе говоря, вы хотите, чтобы ассоциированное значение представляло собой список.РешениеСохраните в элементе хэша ссылку на массив. Используйте push для присоединения новых элементов:push(@{ $hash{"KEYNAME"} }, "new value"); Затем при выводе хэша разыменуйте значение как ссылку на массив: foreach $string (keys %hash) { print "$string: @{$hash{$string}}\n"; КомментарийВ хэше могут храниться только скалярные величины. Впрочем, ссылки и являются скалярными величинами. Они помогают решить проблему сохранения не- скольких ассоциированных значений с одним ключом - в $hash{$key} помещается ссылка на массив, содержащий значения $key. Все стандартные операции с хэшами (вставка, удаление, перебор и проверка существования) могут комбинироваться с операциями массивов (push, splice и foreach). Присвоение ключу нескольких значений осуществляется следующим образом:$hash{"a key"} = [ 3, 4, 5 ]; # Анонимный массив Ключ с ассоциированным массивом используется так: lvalues = @{ $hash{"a key"} }; Для присоединения новых значений к массиву, ассоциированному с конкретным ключом, используется функция push: push @{ $hash{"a key"} }, $value; Классическое применение этой структуры данных - инвертирование хэша, в котором одно значение ассоциируется с несколькими ключами. В хэше, полученном после инвертирования, один ключ ассоциирован с несколькими значениями. Эта проблема рассматривается в рецепте 5.8. Учтите, что запись вида: Presidents = @{ $phone2name{$number} }; при действующей директиве use st rict вызовет исключение, поскольку вы пы та-етесь разыменовать неопределенную ссылку без автоматического создания. Приходится использовать другую формулировку: @residents = exists( $phone2name{$number} ) ? @{ $phone2name{$number} } : О;} > Смотри также ------------------------------
11.3. Получение ссылок на хэшиПроблемаТребуется работать с хэшем по ссылке. Например, ссылка может передаваться ф} ции или входить во внешнюю структуру данных.РешениеПолучение ссылки на хэш:$href = \%hash; $anon_hash = { "key1" => "valuel", "key2" => "value2 ..." }; $anon_hash_copy = { %hash }; Разыменование ссылки на хэш: %hash = %$href; $value = $href->{$key}; @slice = @$href{$key1, $key2, $key3}; # Обратите внимание: стрелки нет! @keys = keys %$hash; Проверка того, является ли переменная ссылкой на хэш: if (ref($someref) ne 'HASH') { die "Expected a hash reference, not $someref\n"; } КомментарийСледующий пример выводит все ключи и значения двух заранее определенных хэшей:foreach $href ( \%ENV, \%INC ) { # ИЛИ: for $href ( \(%ENV,%INC) ) { foreach $key ( keys %$href ) { print "$key => $href->{$key}\n"; } } Операции со срезами хэшей по ссылке выполняются так же, как со срезами массивов. Например: @values = @$hash_ref{"key1", "key2", "key3"}; for $val (@$hash_ref{"key1", "key2", "key3"}) { $val += 7; # Прибавить 7 к каждому значению в срезе хэша } [> Смотри также
11.4. Получение ссылок на функцииПроблемаТребуется создать ссылку для вызова подпрограммы. Такая задача возникает при создании обработчиков сигналов, косвенно-вызываемых функций Tk и указателей па хэши функций.РешениеПолучение ссылки на функцию:$cref = \&func; $cref = sub { ... }; Вызов функции по ссылке: @returned = $cref->(@arguments); @oreturned = &$cref(@arguments); КомментарийЧтобы получить ссылку на функцию, достаточно снабдить ее имя префиксом Кроме того, формулировка sub {} позволяет создавать анонимные функции. Ссылка на анонимную функцию может быть сохранена так же, как и любая другая. В Perl 5.004 появилась постфиксная запись для разыменования ссылок на функции. Чтобы вызвать функцию по ссылке, раньше приходилось писать &$funcname (@ARGS), где $funcname - имя функции. Возможность сохранить имя функции в переменной осталась и сейчас:$funcname = "thefunc"; &$funcname(); однако подобное решение нежелательно по нескольким причинам. Во-первых, в нем используются символические, а не настоящие (жесткие) ссылки, поэтому при действующей директиве use st rict ' refs' оно отпадает. Символические ссылки обычно не рекомендуются, поскольку они не могут обращаться к лексическим, а только к глобальным переменным, и для них не ведется подсчет ссылок. Во-вторых, оно не содержит данных о пакете, поэтому выполнение фрагмента в другом пакете может привести к вызову неверной функции. Наконец, если функция была в какой-то момент переопределена (хотя это происходит нечасто), символическая ссылка будет обращаться к текущему определению функции, а жесткая ссылка сохранит старое определение. Вместо того чтобы сохранять имя функции в переменной, создайте ссылку на нее с помощью оператора \. Именно так следует сохранять функцию в переменной или передавать ее другой функции. Ссылки на именованные функции можно комбинировать со ссылками на анонимные функции: my %commands = ( "happy" => \&joy, "sad" => \&sullen, "done" => sub { die "See ya!" }, "mad" => \&angry, ); print "How are you? "; chomp($string = if ($commands{$string}) { $comroands{$string}->(); } else { print "No such command: $string\n"; } Если вы создаете анонимную функцию, коюрая ссьыапся на лскшчесмю ( у)
переменную из вмещающей области действия, схема подсчета ссылок гарантирует,
что память лексической переменной не будет освобождена при наличии ссылок
на нее:
> Смотри также -------------------------------
11.5. Получение ссылок на скалярыПроблемаТребуется создать ссылку на скалярную величину и работать с ней.РешениеДля создания ссылки на скалярную величину воспользуйтесь оператором \:$scalar_ref = \$scalar; # Получение ссылки на именованный скаляр Чтобы создать ссылку на анонимную скалярную величину (то есть скаляр, не являющийся переменной), присвойте нужное значение через разыменование неопределенной переменной: undef $anon_scalar_ref; $$anon_scalar_ref = 15; Ссылка на скалярную константу создается следующим образом: $anon_scalar_ref = \15; Разыменование выполняется конструкцией ${...}: print ${ $scalar_ref }; # Разыменовать ${ $scalar_ref } .= "string"; # Изменить значение субъекта КомментарийЕсли вам понадобилось создать много новых анонимных скаляров, воспользуйтесь функцией, возвращающей ссылку на лексическую переменную вне области действия, как объяснялось во введении:sub new_anon_scalar { my $temp; return \$temp; } Perl почти никогда не выполняет косвенного разыменования. Исключение составляют ссылки на 4)айловые манипуляторы, программные ссылки на sort И ссылочный аргумент функции bless. Из-за этого для разыменования скалярнов переменной следует снабдить ее префиксом $, чтобы получить все ее содержимое;! $sref = new_anon_scalar(); $$sref = 3; print "Three = $$sref\n"; @array_of_srefs = ( new_anon_scalar(), new_anon_scalar() ); ${ $array[0] } = 6,02е23; ${ $аrrау[1] } = "avocado"; print "\@аrrау contains: ", join(", ", map { $$_ } @аrrау ), "\n"; Обратите внимание на фигурные скобки вокруг $аrrау[0] и $аrrау[1]. Если бы мы попытались ограничиться простым $$аrrау[0], то в процессе разыменования получили бы $аrrау->[0]. Переменная $аrrау интерпретировалась бы как ссылка на массив, поэтому в результате был бы возвращен элемент с нулевым индексом. Приведем другие примеры, в которых фигурные скобки необязательны: $var = 'uptime'; # $var содержит текст $vref = \$var; # $vref "указывает на" $var if ($$vref =~ /load/) {} # Косвенное обращение к $var chomp $$vref; # Косвенное изменение $var Как упоминалось во введении, для определения типа субъекта по ссылке применяется встроенная функция ref. При вызове ref для ссылки на скаляр возвращается строка "SCALAR": # Проверить, содержит ли $someref ссылку на скаляр if (ref($someref) ne 'SCALAR') { die "Expected a scalar reference, not $someref\n"; [> Смотри также -------------------------------
11.6. Создание массивов ссылок на скалярыПроблемаТребуется создать массив ссылок на скаляры. Такая задача часто возникает при передаче функциям переменных по ссылке, чтобы функция могла изменить их значения.РешениеЧтобы создать массив, либо снабдите префиксом \ каждый скаляр в списке:@array_of_scalar_refs = ( \$а, \$b ); либо просто поставьте \ перед всем списком, используя свойство дистрибутивности оператора \: @array_of_scalar_refs = \( $а, $b ); Чтобы получить или задать значение элемента списка, воспользуйтесь конструкцией ${...}: ${ $array_of_scalar_refs[1] } = 12; # $b = 12 КомментарийВ следующих примерах предполагается, что @аrrау - простой массив, содержащий ссылки на скаляры (не путайте массив ссылок со ссылкой на массив). При косвенных обращениях к данным необходимы фигурные скобки.($а, $b, $c, $d) = (1 .. 4); # Инициализировать @аrrау = (\$а, \$b, \$c, \$d); # Ссылки на все скаляры @array = \( $а, $b, $c, $d); # То же самое! ${ $аrrау[2] } += 9; # $c = 12 ${ $array[ $#аrrау ] } *= 5; # $d = 20 ${ $аrrау[-1] } *= 5; # То же; $d = 100 $tmp = $array[-1]; # Использование временной переменной $$tmp *= 5; #$d = 500 Две формы присваивания @аrray эквивалентны - оператор \ обладает свойством дистрибутивности. Следовательно, \ перед списком (но не массивом!) эквивалентно применению \ к каждому элементу списка. Следующий фрагмент изменяет значения переменных, ссылки на которые хранятся в массиве. А вот как работать с массивом без явного индексирования. use Math::Trig qw(pi); # Загрузить константу pi foreach $sref (@array) { # Подготовиться к изменению $a,$b,$c,$d ($$sref **= 3) *= (4/3 * pi); # Заменить объемом сферы } В этом фрагменте используется формула вычисления объема сферы: V = 4/3pir Переменная цикла $s ref перебирает все ссылки @а г гау, а в $$s ref заносятся сами числа, то есть исходные переменные $а, $Ь, $с и $d. Изменение $$sref в цикле приводит к изменению этих переменных. Сначала мы возводим $$sref в куб, а затем умножаем полученный результат на 4/Зтс. При этом используется то обстоятельство, что присваивание в Perl возвращает левостороннее выражение. Это позволяет сцеплять операторы присваивания, как это делается с операторами **= и -. Вообще говоря, анонимные скаляры обычно бесполезны - ведь скалярная B( -личина занимает столько же места, что и ссылка на нее. По этой причине не предусмотрены и специальные конструкции для их создания. Скалярные ссылки существуют только для поддержки синонимов, которые могут быть реализованы и другими способами. > Смотри также -------------------------------
|