Изучаем Perl
5c8b6e8c

Операции над массивами и функции


Присваивание

Вероятно, самая важная операция, проводимая над массивами -— операция присваивания, посредством которой переменной-массиву присваивается значение. Эта операция, как и скалярная операция присваивания, обозначается знаком равенства. 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")



Содержание раздела