Опубликован: 17.08.2006 | Доступ: свободный | Студентов: 5388 / 708 | Оценка: 4.49 / 3.94 | Длительность: 20:58:00
ISBN: 978-5-9556-0078-9
Лекция 5:

Списки и массивы

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >

Срезы

В Perl есть удобная форма обращения к нескольким элементам массива одновременно, называемая срезом массива. Срез (slice) - это набор элементов массива, заданный списком индексов этих элементов. Срез обозначается квадратными скобками после имени массива, в которых перечислены индексы элементов. Поскольку значение среза - это список, при записи среза перед именем массива сохраняется префикс @. Срез массива в частном случае может состоять из одного значения, заданного одним индексом. Вот примеры срезов массивов:

@array[0,1]      # то же, что ($array[0], $array[1])
@array[5..7]     # то же, что ($array[5],$array[6],$array[7])
@array[3,7,1]    # то же, что ($array[3],$array[7],$array[1])
@array[@indexes] # срез, заданный массивом индексов
@array[5]        # список ($array[5]), а не скаляр $array[5]

С помощью срезов удобно одновременно манипулировать значениями нескольких элементов, находящихся в любом месте массива:

# присвоить значения пяти элементам:
@array[5..9] = qw(FreeBSD Linux MacOS NetWare Windows); 
# поменять местами значения 1-го и последнего элементов:
@array[0,-1] = @array[-1,0];
# напечатать элементы с индексами от $start до $finish
print @array[$start .. $finish];

Срезы могут применяться не только к массивам, но и к любым спискам, в том числе литеральным.

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

Для работы с таким популярным типом данных, как массивы, в 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" .

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >
Сергей Крупко
Сергей Крупко

Добрый день.

Я сейчас прохожу курс  повышения квалификации  - "Профессиональное веб-программирование". Мне нужно получить диплом по этому курсу. Я так полагаю нужно его оплатить чтобы получить диплом о повышении квалификации. Как мне оплатить этот курс?

 

Галина Башкирова
Галина Башкирова

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

Системный администратор информационно-коммуникационных» систем.
Мне нужно самой найти тему? или делать по высланным темам