Поиск

Доступ к элементам массива

Выше была описана методика работы с массивами и списками. Теперь рассмотрим вопрос доступа к их индивидуальным элементам. Такой доступ необходим для выполнения поиска элементов в массивах, изменения их значения, а также для добавления и удаления элементов.

Простейший способ получить доступ к содержимому всего массива — использовать его имя в двойных кавычках:

print "@аrrаy";

В этом примере будут распечатаны все элементы массива @array, разделенные пробелами.

Доступ к индивидуальным элементам массива осуществляется посредством их индексов. Индекс элементов массива начинается с 0 и с каждым элементом увеличивается на 1. Каждому элементу массива соответствует свое значение индекса, например:

Количество элементов массива ограничено лишь количеством доступной оперативной памяти. Для доступа к определенному элементу используется синтаксис $имя_ массива[индекс]. Массив не обязательно должен существовать при обращении к его элементам. При необходимости массив создается автоматически. Ниже приведено несколько примеров работы с отдельными элементами массива:

@trees=qw(Дуб Кедр Клен Ясень);
print $trees[O]; | печатает "Дуб"
print $trees[3]; | печатает "Ясень".
$trees[4]='Сосна'

Как видите, когда речь идет об индивидуальных элементах массива $trees, в их названии используется символ $. Но ведь этот символ используется для обозначения скаляров, скажете вы. Символ $ в конструкции $trees[3] говорит о том, что это отдельный скаляр, находящийся в массиве @trees. Скаляры потому и обозначаются символом доллара, что содержат только одно значение. Это важный момент.

В начале этого занятия вы узнали, что скаляры и массивы могут иметь одинаковые имена, но при этом не быть связанными друг с другом. Perl усматривает разницу между скаляром Strees и элементом массива $trees[0], номер которого задается в квадратных скобках. Он понимает, что речь идет о первом элементе массива Gtrees, а вовсе не о скалярной переменной $trees.

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

@trees=qw( дуб Кедр Клен Яблоня Вишня Сосна Персик Ель );
@trees[3,4,6]; | Фруктовые деревья
@conifers=@trees[5,7] | Хвойные деревья
Определение размера массива

Часто требуется определить размер массива, или индекс его последнего элемента. Подобная задача возникает при добавлении или удалении элементов массива. В Perl для решения этой задачи предусмотрено несколько способов. Первый — использование специальной переменной $#имя_ массива. Она возвращает последний допустимый индекс массива, например:

@trees=qw(Дуб Кедр Клен Ябловя Вишня Сосна Персик Ель );
print $#trees;

В этом массиве восемь элементов, но, так как нумерация массивов начинается с нуля, печатается индекс 7. Изменение значения переменной $#trees изменяет длину массива. Уменьшение значения приводит к удалению элементов с большими индексами, а увеличение — добавляет в массив новые элементы. Новые элементы имеют неопределенное значение (undef).

Другой способ нахождения размера массива — использование имени массива в скалярном контексте (т.е. там, где в выражении ожидается скаляр), например:

$size=@array;

Переменная $size теперь содержит количество элементов массива @array. На этом примере мы продемонстрировали, как используется концепция контекста в Perl. О контексте мы поговорим в следующем разделе.

При работе с массивами можно также использовать отрицательные индексы, котрые соответствуют элементам, расположенным с конца массива. Например, $аrrау[-1] — последний элемент массива ваггау, $аггау(-2]— следующий с конца и т.д.
Подробнее о контексте

Что жетакое контекст? Контекст — это некое окружение элемента, помогающее понять, что он собой представляет. Если мы видим человека в одежде хирурга в боль-нице — скорее всего, он врач, а на бале-маскараде — один из гостей.

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

  • магазин за углом;
  • прямой угол;
  • снять угол.

Одно и то же слово, а значения разные. Значение слова зависит от окружающих его слов.

Точно так же и Perl реагирует на контекст. Функции и операторы Perl ведут себя по-разному в зависимости от контекста. Два наиболее важных контекста Perl — это контекст списка и скалярный контекст. Например, оператор присваивания (символ =) можно применять и к массивам, и к скалярам. Тип выражения, находящегося слева (список или скаляр), определяет контекст выражения, находящегося справа. Посмотрите на этот фрагмент кода:

Последняя строка представляет особый интерес, потому что массивы в скалярном контексте возвращают количество элементов массива.

Сравните, как используются переменные $а и $Ь в следующих строках кода (обратите внимание, что оба оператора присваивания делают практически одно и то же):

После выполнения этого кода переменная $а имеет значение 4, а $Ь — 3. Почему так происходит? Массив Ofoo в скалярном контексте возвращает количество своих элементов, которое присваивается переменной $а. А переменной $Ь присваивается значение индекса последнего элемента (не забывайте, что нумерация индекса массива начинается с нуля).

Учитывая, что массив в скалярном контексте возвращает количество своих элементов, легко проверить, пустой массив или нет:

Здесь массив @mydata используется в скалярном контексте, в результате выражение возвращает количество элементов массива, в нашем случае — 4. Условное выражение в операторе if равно 4, а значит, оно истинно и тело блока выполняется.

На самом деле массив gmydata здесь использован в специальном контексте, называемом булевым, или логическим. Это разновидность скалярного контекста со схожим действием. Булев контекст имеет место, когда Perl ожидает получить логическое значение, например в условном выражении оператора if. Еще одна разновидность контекста, называемая пустым контекстом (void), будет рассмотрена на 9-м занятии, "Дополнительные функции и операторы".
Возвращаясь к старой теме

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

Функция print ожидает в качестве аргумента список. Неважно, в каком контексте формируется этот список. Поэтому функция print с массивом f!foo, переданным вкачестве аргумента, распечатывает элементы этого массива, находящегося в контексте списка:

print @foo;

Для навязывания скалярного контекста можно использовать псевдофункцию scalar:

print scalar(@foo);

Здесь печатается количество элементов массива @foo. Функция scalar определяет скалярный контекст для ifoo, поэтому массив возвращает количество своих элементов, выводимое впоследствии функцией print.

Рассмотренной нами на 2-м занятии, "Строительные блоки Perl: числа и строки",
функции chomp может быть передан в качестве аргумента как скаляр, так и массив. В
скалярном аргументе удаляется завершающий символ-разделитель записей. Если ар
гументом является массив, символ-разделитель удаляется в конце каждого скалярного
элемента. ..

Вы уже знаете, как прочитать данные, вводимые с клавиатуры, используя оператор <STDIN>. Угловые скобки — это специальный оператор Perl, который, в зависимости от контекста, ведет себя по-разному. В скалярном контексте этот оператор читает с терминала одну строку. В контексте списка этот оператор читает весь поток данных, поступающих с терминала, до символа конца файла и помешает затем все данные в список. Сравните: .

Какое значение получает переменная $а в третьем примере? Помните, выше мы говорили, что если в левой части оператора присваивания находится список, причем количество его элементов меньше, чем в списке, находящемся в правой части, то избыточные элементы правой части отбрасываются. Таким образом, в третьем примере будут считаны все данные с терминала, но в переменную $а помещается только первая введенная строка. . . . ...

Что такое конец файла? После окончания ввода с терминала надо дать знать Perl, что ввод данных завершен. Для этого нужно набрать символ конца файла (EOF). Что это за символ, зависит от операционной системы. В UNIX таким символом является <Ctri+D>, помещенный в начале строки. В MS-DOS или Windows признаком конца файла являются два идущих подряд символа <Ctrl+Z>, которые могут располагаться в любом месте текста.

Оператор повторения, рассмотренный на 1-м занятии, "Начало работы с Perl", в контексте списка ведет себя специфическим образом. Если левый операнд оператора повторения взят в скобки и сам оператор находится в контексте списка, то возвращается список с элементами, соответствующими левому операнду. В этом примере создается список из 100 звездочек:

@stars= С*") х 100;

Левый операнд "*" оператора повторения находится в скобках, а значение полученного выражения присваивается массиву, что и определяет контекст списка. Такой синтаксис применяется для инициализации элементов массива одинаковыми значениями.

Другой часто используемый оператор, который вообще мало кто относит к категории операторов, — запятая (,). Пока мы лишь использовали ее для разделения элементов списка литералов, например:

@pets=( 'Кошка', 'Собака', 'Рыбки', 'Канарейка', 'Игуана');

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

$last_pet=('Кошка', 'Собака', 'Рыбки', 'Канарейка', 'Игуана');
| Совсем не то, что вы могли подумать!

Здесь названия домашних животных, расположенные справа от оператора присваивания, с точки зрения Perl не являются списком. Это группа строковых литералов, значение которых вычисляется в скалярном контексте слева направо (из-за скаляра $last_pet, расположенного в левой части). В результате переменной $last_pet присваивается значение 'Игуана'.

Другой пример — функция localtime, в зависимости от контекста, имеет два абсолютно различных варианта поведения. В скалярном контексте функция localtime возвращает форматированную строку текущего времени. Например, оператор print scalar (localtime) напечатает что-то похожее на Thu Apr 13 10:14:45 2000. В контексте списка функция localtime возвращает список элементов, описывающих текущее время:

($sec, $min, $hour, $mday, $mon, $year_off, $wday, $yday, $isdst)=localtime

Значения этих элементов приведены в табл. 4.1.

Большинство проблем 2000 года в программах на Perl были связаны с неправильным использованием параметра $year_off, возвращаемого функцией localtime. Чтобы получить текущий год, большинство программистов добавляло к значению этого параметра строку '19'. Однако следует учитывать тот факт, что разница между текущим и 1900 годом в 1999 году равна 99, в 2000 году— 100. Арифметическое же сложение этого значения с 1900 будет корректно работать и после 2000 года. Сам Perl давно избавлен от ошибки Y2K, но использование параметра $year_off с префиксами '19' или '20' действительно может привнести ее в вашу программу.

Как же узнать, какой контекст создают аргументы определенной функции или оператора и как эта функция или оператор ведет себя в различных контекстах? Это не так-то просто, поскольку единой методики нет. В документации представлены все функции и операторы с объяснением их поведения в зависимости от нескольких факторов. В дальнейшем мы будем обращать ваше внимание, если встретившаяся впервые в этой книге функция специфическим образом ведет себя в различных контекстах.