Язык программирования Perl

Функции работы с массивами


Для работы с таким популярным типом данных, как массивы, в Perl существует много удобных функций. Когда требуется организовать обработку списка, поочередно извлекая из него элементы, начиная с первого, применяется встроенная функция shift. Она удаляет из массива первый элемент, возвращая его значение. Когда shift применяется к пустому списку, она возвращает неопределенное значение:

$first = shift @array; # извлечь первый элемент в $first # синоним: ($first, @array) = @array;

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

while (my $first = shift @array) { # пока @array не опустеет print "Обработан элемент $first."; print "Осталось ", scalar @array, " элементов\n"; }

Обратите внимание, что для вывода текущего размера массива нужно использовать scalar @array потому, что иначе print воспримет @array как список для печати и выведет значения массива. Существует противоположная shift функция unshift, которая вставляет свои аргументы в массив перед первым элементом, сдвигая существующие элементы вправо.

unshift @array, $e1,$e2,$e3; # вставить значения в начало # синоним: @array = ($e1,$e2,$e3, @array);

С помощью массива можно организовать стек, данные в котором обрабатываются по алгоритму LIFO ("last in, first out", "последним пришел - первым ушел"). Для добавления данных в стек применяется операция push, которая добавляет элементы в конец массива:

push @stack, $new; # добавить элемент в конец массива # синоним: @stack = (@stack, $new);

Для извлечения одного значения из стека служит встроенная функция pop, которая удаляет последний элемент массива, возвращая его значение:

$last = pop @stack; # изъять последний элемент массива

При помощи комбинации функций push и shift можно организовать список, реализующий очередь данных, у которой элементы добавляются в конец, а извлекаются из начала (в соответствии с алгоритмом FIFO, "first in, first out", "первым пришел - первым ушел").

Для удаления или замены подсписка в массиве можно использовать функцию splice, которая удаляет идущие подряд элементы массива, заданные индексом первого элемента и количеством удаляемых элементов, и заменяет их новым списком (если он указан), возвращая список удаленных элементов.


@array = (1..7); # исходный массив $offset = 2; $size = 4; # смещение и размер удаляемого списка @deleted = splice @array, $offset, $size, qw(новый список); # в @array теперь (1, 2, 'новый', 'список', 7) # в @deleted попали 4 удаленных элемента (3, 4, 5, 6)



Если список для вставки не указан, то подсписок от элемента с индексом $offset в количестве $size элементов просто удаляется.

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

@unsorted = (12, 1, 128, 2, 25, 3, 400, 53); @sorted = sort @unsorted; # в @sorted будет (1, 12, 128, 2, 25, 3, 400, 53)

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

@sorted = sort {$a <=> $b } @unsorted; # в @sorted будет (1, 2, 3, 12, 25, 53, 128, 400)

В блоке сравнения переменные $a и $b содержат значения двух текущих сравниваемых элементов. Для выполнения сортировки по убыванию достаточно поменять переменные местами {$b <=> $a }. Помните, что для сортировки в обратном порядке строковых значений нужно применить операцию сравнения строк {$b cmp $a }. Вместо блока можно вызвать пользовательскую подпрограмму, выполняющую сколь угодно сложные сравнения элементов сортируемого списка.

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

@array = qw(Do What I Mean); # исходный список @backwards = reverse @array; # остается неизменным # в @backwards будет ('Mean', 'I', 'What', 'Do')

Вложенный вызов функций позволяет сначала отсортировать список, а потом переставить элементы в обратном порядке:



@backwards = reverse(sort(@array)); # в @backwards будет ('What', 'Mean', 'I', 'Do')

Обратите внимание, что во всех приведенных примерах по желанию программиста аргументы функций можно указывать в круглых скобках, но делать это не обязательно. Имея в своем распоряжении мощные примитивы для работы с массивами, подобные reverse или splice, программист может легко решать весьма нетривиальные задачи. Это подтверждает короткая программа на Perl, выполняющая циклический сдвиг массива тремя вызовами функции reverse:

my @array = qw/Н А Ч А Л О К О Н Е Ц/; # исходный массив my $i = 3; # сдвиг массива ВЛЕВО на 3 элемента my $n = @array; # число элементов массива # алгоритм сдвига Кена Томпсона (1971) @array[0 ..$i-1] = reverse @array[0 .. $i-1]; @array[$i .. $n-1] = reverse @array[$i .. $n-1]; @array[0 .. $n-1] = reverse @array[0 .. $n-1]; print "@array\n"; # результат: А Л О К О Н Е Ц Н А Ч

Функция map позволяет выполнить действия над всеми элементами массива, поэтому ее нередко используют вместо цикла. У этой функции есть две формы вызова:

@result = map ВЫРАЖЕНИЕ, СПИСОК @result = map БЛОК СПИСОК

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

@result = map $_*10, (11, 32, 55); # работа со списком # в @result будет (110, 320, 550)

При работе map специальная переменная $_ локально устанавливается как синоним текущего элемента списка, поэтому изменение переменной $_ приводит к изменению соответствующего элемента массива. Таким способом можно изменять значения элементов массива. В этом примере воспользуемся блоком, куда поместим операторы вычисления нового значения (если значение элемента больше 20, оно будет удесятеряться):

@array = (11, 32, 55); # исходный массив @result = map {if ($_ > 20) {$_*=10;} else {$_;} } @array; # в @result будет (11, 320, 550)

Список можно преобразовать в строку с помощью встроенной функции join, которая преобразует каждый элемент списка к строке, объединяет отдельные элементы списка в одну строку, вставляя между элементами указанный разделитель, и возвращает полученную строку в качестве результата. Например:



@array = (5..10); # объединяемый список $delimiter = ':'; # разделитель элементов списка в строке $string = join $delimiter, @array; # объединение в строку # теперь $string содержит '5:6:7:8:9:10'

Обратную операцию разделения строки по образцу на список строк выполняет встроенная функция split. Она разделяет строку по указанному разделителю и возвращает список составляющих строк. Можно ограничить число разделяемых подстрок, тогда строка будет разделена не более, чем на это число элементов. Например:

$string = '5:6:7:8:9:10'; # исходная строка $delimiter = ':'; # разделитель подстрок $limit = 3; # число элементов @strings = split $delimiter, $string, $limit; # разделение # в @strings содержится ('5', '6', '7:8:9:10')

Функция split имеет гораздо больше возможностей, о которых будет сказано в лекции, посвященной регулярным выражениям. Подробно познакомиться с которыми можно из системной документации с помощью утилиты perldoc (после флага -f указывается имя искомой функции):

perldoc -f split

Пользовательские функции и процедуры, как встроенные функции, тоже могут обрабатывать списки: принимать списки параметров и возвращать список значений. Об этом будет подробнее рассказано в лекции 12.


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