Операции над массивами и функции
Присваивание
Вероятно, самая важная операция, проводимая над массивами -— операция присваивания, посредством которой переменной-массиву присваивается значение. Эта операция, как и скалярная операция присваивания, обозначается знаком равенства. Perl определяет тип присваивания (скалярное или для массива), анализируя, какой переменной присваивается значение — скалярной или массиву. Например:
@fred = (1,2,3); # массив fred получает трехэлементное литеральное значение Qbarney = @fred; # теперь оно копируется в @barney
Если переменной-массиву присваивается скалярное значение, оно становится единственным элементом этого массива:
@huh =1; #1 автоматически становится списком (1)
Имена переменных-массивов могут входить в списочный литерал. При вычислении значений такого списка Perl заменяет имена массивов текущими значениями, например:
@fred = qw(one two);
@barney = (4,5,@fred,6,7) ; # @barney превращается в (4,5,"one","two",6,7) @barney = (8,@barney); # в начале списка элементов Qbarney ставится 8
# и "last" в конце. Qbarney = (@barney,"last") ; # @barney превращается в
# (8,4,5,"one","two",6,7,"last")
Отметим, что вставленные подобным образом элементы массива находятся на том же уровне иерархии, что и остальная часть литерала: список не может содержать другой список в качестве элемента*.
Если списочный литерал содержит только ссылки на переменные (а не выражения), то этот литерал также можно рассматривать как переменную. Другими словами, такой списочный литерал можно использовать в левой части операции присваивания. Каждая скалярная переменная в списочном литерале принимает соответствующее значение из списка, стоящего в правой части операции присваивания. Например:
($а,$Ь,$с) = (1,2,3); # присвоить 1 переменной $а, 2 — переменной $Ь,
# 3 — переменной $с ($a,$b) = ($b,$a); # поменять местами $а и $Ь ($d,@fred) = ($a,$b,$c) # присвоить $d значение $а, a @fred — значение ($Ь,$с) ($e,@fred) = @fred; # переместить первый элемент массива @fred
# в переменную $е. В результате @fred = ($с),
# а $е = $Ь
Если число присваиваемых элементов не соответствует числу переменных, то лишние значения (стоящие справа от знака равенства) просто отбрасываются, а все лишние переменные (слева от знака равенства) получают значение undef.
Переменная-массив, входящая в литеральный список, должна стоять в нем последней, потому что переменные-массивы очень "прожорливы" и поглощают все оставшиеся значения. (Конечно, после нее можно поставить и другие переменные, но всем им будет присвоено значение undef.)
Если переменная-массив присваивается скалярной переменной, то присваиваемое число является размером массива, например:
@fred = (4,5,6); # инициализация массива @fred $а = @fred; # $а получает значение 3, текущий размер массива @fred
Размер возвращается и в том случае, если имя переменной-массива используется там, где должно быть скалярное значение. (В разделе "Скалярный и списочный контексты" мы увидим, что это называется использованием имени
* Хотя ссылка на список и может быть элементом списка, на самом деле это не означает использование списка в качестве элемента другого списка. Впрочем, работает это почти так же, что позволяет создавать многомерные массивы. См. главу 4 книги Programming Perl и man-страницу perllol( I).
массива в скалярном контексте.) Например, чтобы получить число на единицу меньшее, чем размер массива, можно использовать @fred-l, так как скалярная операция вычитания требует наличия скаляров в обеих частях. Обратите внимание на следующий пример:
$а = @fred; # переменной $а присваивается размер массива @fred ($а) = @fred; # переменной $а присваивается первый элемент @fred
Первое присваивание — скалярное, и массив @fred рассматривается как скаляр, поэтому значение переменной $а будет равно размеру массива. Второе присваивание — для массива (несмотря на то, что требуется всего одно значение), поэтому переменной $а в качестве значения присваивается первый элемент массива @fred, а остальные элементы просто отбрасываются.
В результате выполнения присваивания для массива мы получаем значение, представляющее собой список. Это позволяет делать "каскадиро-вание". Например:
@fred = (Qbarney = (2,3,4)); # @fred и @barney получают значения (2,3,4) @fred = @barney = (2,3,4); # то же самое
Обращение к элементам массива
До сих пор мы рассматривали массив в целом, добавляя и удаляя значения с помощью присваивания для массива. Многие полезные программы так и построены — с использованием массивов, но без обращения к их элементам. Perl, однако, предоставляет и традиционный способ обращения к элементам массива по их числовым индексам.
Элементы массива нумеруются последовательными целыми числами с шагом 1, начиная с нуля*. Первый элемент массива @fred обозначается как $fred[0]. Обратите внимание: при ссылке на элемент вместо знака @ в имени массива используется знак $ . Это объясняется тем, что обращение к элементу массива идентифицирует скалярную переменную (часть массива), которой в результате может быть присвоено значение или которая используется в выражении, например:
@fred = (7,8,9);
$b = $fred[0]; # присвоить $Ь значение 7 (первый элемент массива @fred) $fred[0] = 5; # теперь @fred = (5,8,9)
Точно так же можно обращаться к другим элементам:
$с = $fred[l]; # присвоить $с значение 8
$fred[2]++; # инкрементировать третий элемент массива @fred
$fred[l] +=4; # прибавить 4 ко второму элементу
($fred[0],$fred[l]) = ($fred[l],$fred[0]) ;
# поменять местами первые два элемента
* Значение индекса первого элемента можно изменить на что-нибудь другое (например, на "1"). Это, однако, повлечет за собой очень серьезные последствия: может запутать тех, кто будет сопровождать ваш код, и повредить программы, которые вы заимствуете у других. По этой причине настоятельно рекомендуем считать такую возможность отсутствующей.
Обращение к списку элементов одного массива (как в последнем примере) называется срезом" и встречается достаточно часто, поэтому для него есть специальное обозначение.
@fred[0,l] * то же, что и ($fred[0],$fred[l]) @fred[0,l] = @fred[l,0] # поменять местами первые два элемента @fred[0,l,2] = @fred[l,l,l] # сделать все три элемента такими, как второй @fred[l,2] = (9,10); # заменить последние два значения на 9 и 10
Обратите внимание: в этом срезе используется не $, а @. Это вызвано тем, что вы создаете переменную-массив (выбирая для этого часть массива), а не скалярную переменную (обращаясь к одному элементу массива).
Срезы работают также с литеральными списками и вообще с любой функцией, которая возвращает список:
@who = (qw(fred barney betty wilma))[2,31;
# как @х = qw(fred barney betty wilma); @who = @x[2,3]
Значения индексов в этих примерах — литеральные целые, но индекс может быть и любым выражением, которое возвращает число, используемое затем для выбора соответствующего элемента:
@fred ” (7,8,9) ;
$а = 2;
$b " $fred[$a); f как $fred[2], или 9
$с = $fred[$a-l]; # $c получает значение $fred[l], или 8
($с) = (7.8,9)[$а-1]; # то же самое, но с помощью среза
Таким образом, обращение к массивам в Perl-программах может производиться так же, как во многих других языках программирования.
Идея использования выражения в качестве индекса применима и к срезам. Следует помнить, однако, что индекс среза — список значений, поэтому выражение представляет собой выражение-массив, а не скаляр.
Sfred = (7,8,9); f как в предыдущем примере Bbarney =(2,1,0);
Obackfred = @fred[@barney];
# то же, что и @fred[2,l,0], или ($fred[2],$fred[l],$fred[0]), или (9,8,7)
Если обратиться к элементу, находящемуся за пределами массива (т.е. задав индекс меньше нуля или больше индекса последнего элемента), то возвращается значение undef. Например:
@fred = (1,2,3) ;
$barney = $fred[7]; # $barney теперь имеет значение undef
* Это перевод английского термина slice, использованный в данной книге. Более точно суть операции можно было бы выразить термином вырезка, но мы остановились на слове срез, чтобы не вызывать гастрономических ассоциаций у любителей мясных блюд — Прим. ред.
Присваивание значение элементу, находящемуся за пределами текущего массива, автоматически расширяет его (с присваиванием всем промежуточным значениям, если таковые имеются, значения undef). Например:
@fred = (1,2,3);
fred[3] = "hi"; # @fred теперь имеет значение (1,2,3,"hi") $fred[6] = "ho"; # @fred теперь имеет значение
# (1,2,3,"hi",undef,undef,"ho")
Присваивание значения элементу массива с индексом меньше нуля является грубой ошибкой, потому что происходит такое, скорее всего, из-за Очень Плохого Стиля Программирования.
Для получения значения индекса последнего элемента массива @fred можно использовать операцию $#fred. Можно даже задать это значение, чтобы изменить размер массива @fred, но это, как правило, не нужно, потому что размер массива увеличивается и уменьшается автоматически.
Использование отрицательного индекса означает, что следует вести обратный отсчет от последнего элемента массива. Так, последний элемент массива можно получить, указав индекс -1. Предпоследний элемент будет иметь индекс -2 и т.д. Например:
@fred = ("fred", "wilma", "pebbles", "dino");
print $fred(-l]; # выводит "dino" print $#fred; # выводит 3 print $fred[$#fred]; # выводит "dino"
Функции push и pop
Одним из распространенных вариантов использования массива является создание стека данных, где новые значения вводятся и удаляются с правой стороны списка. Эти операции применяются довольно часто, поэтому для них предусмотрены специальные функции:
push(@mylist,$newvalue); # означает Omylist =• (@mylist,$newvalue) $oldvalue = pop($mylist); # удаляет последний элемент из @mylist
Если в функцию pop введен пустой список, она возвращает undef, не выдавая, в соответствии с принятым в Perl этикетом, никакого предупреждающего сообщения.
Функция push также принимает список значений, подлежащих помещению в стек. Эти значения вводятся в конец списка. Например:
@mylist = (1,2,3);
push(@mylist,4,5,6) ; # @mylist = (1,2,3,4,5,6)
Отметим, что первый аргумент должен быть именем переменной-масси-ва, потому что для литеральных списков функции push и pop смысла не имеют.
Функции shift и unshift
Функции push и pop действуют в "правой" части списка (части со старшими индексами). Функции unshift и shift выполняют соответствующие действия в "левой" части списка (части с младшими индексами). Вот несколько примеров:
unshift(@fred,$a); # соответствует Bfred = ($a,@fred);
unshift (@fred,$a,$b,$c); # соответствует @fred = ($а,$b,$c,@fred);
$х = shift(@fred); # соответствует ($x,@fred) = @fred;
# с реальными значениями @fred = (5,6,7) ;
unshift(@fred,2,3,4); # @fred теперь имеет значение (2,3,4,5,6,7) $х = shift(@fred) ;
# $х получает значение 2, @fred теперь имеет значение (3,4,5,6,7)
Как и функция pop, функция shift, если в нее ввести пустую перемен-ную-массив, возвращает значение undef.
Функция reverse
Функция reverse изменяет порядок следования элементов аргумента на противоположный и возвращает список-результат. Например:
@а = (7,8,9) ;
@b = reverse(@a); t присваивает @Ь значение (9,8,7) @b == reverse (7,8,9); # делает то же самое
Обратите внимание: список-аргумент не изменяется, так как функция reverse работает с копией. Если вы хотите изменить порядок элементов "на месте", список-аргумент следует затем присвоить той же переменной:
@Ь = reverse (@b); t присвоить массиву @Ь его же значения,
# но расположить его элементы в обратном порядке
Функция sort
Функция sort сортирует аргументы так, как будто это отдельные строки, в порядке возрастания их кодов ASCII. Она возвращает отсортированный список, не изменяя оригинал. Например:
@х ° sort("small","medium","large") ;
# @х получает значение "large", "medium", "small" @у = (1,2,4,8,16,32,64) ;
@у = sort (@y); # @у получает значение 1, 16, 2, 32, 4, 64, 8
Отметим, что сортировка чисел производится не по их числовым значениям, а по их строковым представлениям (1,16, 2, 32 и т.д.). Изучив главу 15, вы научитесь выполнять сортировку по числовым значениям, по убыванию, по третьему символу строки и вообще каким угодно методом.
Функция chomp
Функция chomp работает не только со скалярной переменной, но и с массивом. У каждого элемента массива удаляется последний пробельный символ. Это удобно, когда вы, прочитав несколько строк как список отдельных элементов массива, хотите одновременно убрать из всех строк символы новой строки. Например:
@stuff = ("hello\n","world\n","happy days") ;
chomp(@stuff); # Sstuff теперь имеет значение ("hello","world","happy day")