PHP

Срезы

Часто оказывается, что из всего списка требуется обработать лишь часть элементов. Допустим, библиотека хранит информацию о своих читателях в большом файле. Каждая строка файла описывает одного читателя в виде шести полей, разделенных символом «:»: имя, номер библиотечной карточки, домашний адрес, домашний телефон, рабочий телефон и количество выданных книг. Небольшой фрагмент файла выглядит примерно так:

fred flintstone:2168:301 Cobblestone Way:555-1212:555-2121:3
barney rubble:709918:3128 Granite Blvd:555-3333:555-3438:0


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

while (<FILE>) {
chomp;
my @items = split /:/;
my($card_num, $count) = ($items, $items);
... # Теперь работаем с этими двумя переменными
}


Но массив @items больше ни для чего не нужен; он выглядит совершенно лишним. Может быть, результат split лучше будет присвоить списку скаляров:

my($name, $card_num, $addr, $home, $work, $count) = split /:/;


Нам удалось обойтись без ненужного массива @items, но теперь в программе появились четыре ненужные скалярные переменные. В таких ситуациях некоторые программисты используют фиктивные имена вида $dummy_1, показывающие, что после разбивки строки значение этого элемента не используется. Но Ларри решил, что это слишком хлопотно, поэтому в Perl был добавлен специальный способ использования undef. «Присваивание» элемента списка значению undef говорит о том, что соответствующий элемент исходного списка попросту игнорируется:

my(undef, $card_num, undef, undef, undef, $count) = split /:/;


Удобнее? Да, из программы исчезают ненужные переменные. Впрочем, есть и недостаток: теперь нам придется считать undef, чтобы определить, какому элементу соответствует $count. А при большом количестве элементов в списке решение становится слишком громоздким. Например, некоторые программисты, которые хотели извлечь из данных stat только значение mtime, писали код следующего вида:

my(undef, undef, undef, undef, undef, undef, undef,
undef, undef, $mtime) = stat $some_file;


Стоит ошибиться с подсчетом undef, и вместо mtime вы получите atime или ctime; такие ошибки весьма усложняют отладку. Но существует более удобное решение: Perl может индексировать списки по аналогии с массивами. Результат называется срезом (slice) списка. Значение mtime является элементом с индексом 9 в списке, возвращаемом stat2, и его можно получить по соответствующему индексу:

my $mtime = (stat $some_file);


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

my $mtime = stat($some_file); # Синтаксическая ошибка!


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

my $card_num = (split /:/);
my $count = (split /:/);

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

my($card_num, $count) = (split /:/)[1, 5];


Команда извлекает из списка элементы с индексами 1 и 5, возвращая их в виде списка из двух элементов. Присваивая этот список двум переменным my, мы получаем ровно то, что хотели. Срез выполняется только один раз, и значения сразу двух переменных задаются в простой и понятной записи. Срезы часто оказываются самым простым способом извлечения нескольких элементов из списка. В следующем примере из списка извлекается первый и последний элемент; при этом мы используем тот факт, что индекс –1 соответствует последнему элементу:

my($first, $last) = (sort @names)[0, -1];


Индексы могут следовать в произвольном порядке и даже повторяться. В этом примере извлекаются 5 элементов списка, содержащего 10 элементов:

my @names = qw{ zero one two three four five six seven eight nine };
my @numbers = ( @names )[ 9, 0, 2, 1, 0 ];
print "Bedrock @numbers\n"; # Bedrock nine zero two one zero
Теги:
расширенные возможности
Похожие новости:
Добавлено: 20 Июля 2018 21:35:33 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...