Массивы скаляров

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

Создание массивов скаляров

Для задания значений элементов массива можно использовать индекс или специальную синтаксическую конструкцию, называемую конструктором массива скаляров.
В программе для хранения массивов используются переменные, имена которых начинаются с символа @, за ним следует правильный идентификатор переменной. Однако чтобы воспользоваться «операцией» индексирования, которая синтаксически определяется целым числом или скалярной переменной в квадратных скобках, непосредственно следующих за идентификатором переменной массива, следует заменить символ @ на символ $. В Perl последовательно проводится политика использования первого символа в имени переменной для обозначения ее типа, а так как операция индексирования возвращает скалярное значение, то и требуется подобная замена первого символа. Отметим, что индексы массивов скаляров начинаются с нуля. Таким образом, появление в программе следующих операторов приводит к объявлению массива скаляров @аrray, содержащего два элемента:
$аrrау[0] = 1;
$array[l] = "two";
Допускается использование отрицательных индексов для обращения к элементам массива в обратном порядке, однако следует учитывать, что при этом хотя бы один элемент массива должен быть определен. Индекс -1 соответствует последнему элементу массива, -2 — предпоследнему и т. д. Например, изменить значение последнего элемента массива @аrrау можно так:
$аrray[-1] = "last";
Массивы в Perl являются динамическими. Добавление нового элемента автоматически увеличивает его размер, причем добавление не обязательно должно быть «непрерывным». Это означает, что в массиве можно задать какой-либо элемент с большим индексом, «пропустив» все предыдущие, и подобное действие не приведет к ошибке компиляции. Размер массива будет автоматически преобразован в соответствии с наибольшим использованным для элемента индексом, а не заданные элементы получат значения, равные пустой строке " ". Попытка использования значений таких элементов в выражениях при включенном режиме отображения предупреждающих сообщений интерпретатора (ключ -w) приведет к предупреждению об использовании неинициализированных значений элементов вида:
Use of uninitialized value in multiplication (*) at ex03-02.pl line 5.
Получить максимальный индекс инициализированного элемента массива можно с помощью специальной операции
$#идентифинатор_массива;
Если необходимо получить количество элементов массива, то, естественно, следует к полученному значению добавить 1, так как индексация начинается с нуля. Эта операция, примененная к пустому массиву (как создать подобный массив, см. далее), возвращает значение -1. В следующем фрагменте программы определяется массив с инициализированными первым и последним (с индексом 5) элементами и подсчитывается количество его элементов:
$arrаy[0] = 1; 
$array[5] = "five";
print $#array+1;         # Напечатает 6
Присваиванием конструкции $#идентификатор_массива числового значения можно увеличить или уменьшить размер массива скаляров. Присваивая значение большее, чем текущий максимальный размер индекса, мы просто расширяем диапазон допустимых символов. Элементы массива с новыми индексами будут существовать, но они не инициализированы. Присваивая значение меньше текущего максимального значения индекса, мы тем самым уменьшаем размер массива, уничтожая все его элементы с индексом, большим присвоенного значения:
$array = (100, 200, 300, 400);
$#array = 1;            # Теряем $array[2] и $array[3]
print $#array, "\n":    # Отобразит 1
print $array[2];        # Ошибка. Предупреждение об использовании
                        # неинициализированного значения
print $#array, "\n";    # Отобразит 1
Задание каждого элемента массива с помощью операции присваивания — достаточно утомительное занятие. В Perl существует специальная синтаксическая конструкция — конструктор массива, с помощью которой можно задать в одном операторе присваивания значения нескольких элементов. Конструктор массива представляет собой заключенный в круглые скобки список скалярных значений, разделенных запятыми:
(скаляр1,  скаляр2, ... , скалярN)
В операторе присваивания переменной массива подобного конструктора каждый элемент списка представляет значение соответствующего элемента массива. Следующий оператор с конструктором
@array = (1, 2, "three", 4):
эквивалентен последовательности четырех операторов присваивания значений элементов массива с помощью индекса:
$array[0] = 1;
$array[l] = 2;
$array[2] = "three";
$array[3] = 4;
ВНИМАНИЕ В операторе присваивания значений элементам массива с помощью конструктора используется префикс @ в имени переменной массива, а при использовании операции индексирования - $.
Массив может состоять из неограниченного числа элементов, а может не иметь ни одного. В этом случае его называют пустым, и для задания такого массива используется пустой конструктор ( ).
Операцию индексирования можно применять и к конструктору массива. Конечно, для конструктора массива эта операция может оказаться бессмысленной — ведь конструктор появляется в операторе присваивания, и нет смысла в выделении элемента массива из конструктора, если после инициализации элементов массива к ним можно будет обращаться с использованием идентификатора переменной массива. Однако эта операция полезна при работе с некоторыми стандартными функциями Perl, возвращающими целый список значений. Например, функция stat( ) возвращает информацию о файле, дескриптор которого задан в качестве параметра этой функции, в виде списка значений его характеристик. Чтобы использовать индекс для доступа к восьмому элементу этого списка, который содержит размер файла, следует заключить в круглые скобки вызов этой функции и применить операцию индексирования:
open(F1, "ex03-01.pl");
$dev = ( stat(Fl) )[7]; print $dev;
Важно запомнить, что индекс можно применять к массивам скаляров и их конструкторам, а не к спискам. Если список не заключить в круглые скобки, то это приведет к ошибке компиляции сценария, как в следующем фрагменте программы:
$dev = stat(F1)[7]; # Неправильное использование индекса !!
print $dev;
В конструкторе массива в качестве элемента списка можно использовать другой конструктор, но мы не получим, как можно было бы подумать, элемент массива, являющийся массивом. Дело в том, что в этом случае все элементы списка такого «вложенного» конструктора массива будут просто вставлены как дополнительные элементы внешнего конструктора массива, увеличив количество элементов списка этого конструктора на количество элементов «вложенного» конструктора. Например, конструктор
(1, (5, 6), 8)
эквивалентен следующему:
(1, 5, 6, 8)
Если необходимо задать большой массив целых чисел, то можно в конструкторе массива использовать операцию «диапазон». Ее синтаксис:
первое_число..последнее__число
Результатом выполнения операции является список целых чисел, начинающийся с первого числа и завершающийся последним числом. Если в качестве начала и конца диапазона заданы вещественные числа, то они преобразуются к целым простым отбрасыванием дробных частей. Например, данный конструктор
@m = (-5.5..7.9);
создаст массив, элементами которого являются следующие целые числа:
-5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
Операндами в операции «диапазон» могут быть и строковые данные, содержащие алфавитно-цифровые символы. В этом случае увеличение на единицу означает увеличение на единицу ASCII-кода символа строки. Примеры операции «диапазон» со строковыми данными показаны ниже:
"a".."d"       # Результат: "а", "Ь", "с", "d"
"BCY".."BDB"   # Результат: "BCY", "BCZ", "BDA", "BDB"
Значения переменных массива, как и скалярных переменных, можно подставлять в строки, ограниченные двойными кавычками. При этом под значением переменной массива в операции ее подстановки в строку понимается список значений всех элементов массива, разделенных пробелами. Результатом выполнения следующих операторов:
аrray =(0..9, "\n\t\t\t", "A".."F");
print "Шестнадцатеричные цифры: @array";
будет вывод строк:
Шестнадцатеричные цифры: 0123456739 
                         А В С D E F
Все правила определения идентификатора переменной массива после префикса @ при выполнении операции подстановки полностью совпадают с правилами определения идентификатора скалярной переменной после префикса $, за исключением того, что между префиксом и идентификатором не должно быть пробелов.
Подстановка значения переменной массива скаляров — удобный способ его распечатки. Дело в том, что если переменную массива просто передать в функцию печати print, то все значения элементов массива будут распечатаны без пробелов между ними, тогда как заключение имени массива в двойные кавычки приводит к хорошо читаемому результату. Для сравнения отобразим предыдущий массив @аrrау с помощью оператора
print @аrrаy; 
Результат будет следующим:
Шестнадцатеричные цифры: 0123456789 
                         ABCDEF
Правила подстановки в строку значения элемента массива, выбранного с помощью индекса, полностью совпадают с правилами подстановки значения скалярной переменной. Это и очевидно, так как элемент массива представлен скаляром.
Однако при подстановке значения элемента массива может возникнуть одна интересная проблема. Язык Perl позволяет создавать с одним и тем же идентификатором переменные разных типов. Например, в программе могут сосуществовать $var (скалярная переменная), @var (переменная массива скаляров) и %var (переменная хэш-массива). Это связано с тем, что в механизме реализации переменных предусматривается создание трех пространств имен для переменных базовых типов: одно для скаляров, второе для массивов и третье — для хэш-массивов. Поэтому конфликта возникнуть не может. Но значение какого объекта будет подставлено в строку "$var[0]", если в программе определена и скалярная переменная $var, и массив @var?
Правильный ответ — значение элемента массива. Синтаксический анализатор будет выделять правильную лексему до тех пор, пока не встретится пробел или символ, добавление которого в уже полученную строку не приведет к созданию лексемы. А что же делать, если необходимо напечатать строку вида:
Первый элемент; $var[0] содержит "Иванов"
причем идентификатор массива хранится в переменной $var?
Для решения подобной и схожих проблем можно предложить три способа решения:
"${var}[0]"   # Фигурные скобки ограничивают символы.
              # рассматриваемые интерпретатором
              # как единое целое с символом $. 
"$var\[0]"	  # Обратная дробная черта ограничивает
              # идентификатор переменной, 
"$var"."[0]"  # Конкатенация строк (операция ".")
              # позволяет однозначно интерпретировать
              # переменную в первой строке.
Таким образом, следующая последовательность операторов решит поставленную нами задачу вывода строки:
$var = "var";
@var = ("Иванов", "Петров");
print "Первый элемент \$$var\[0] содержит \"$var[0]\"";
При работе с массивом иногда необходимо выделять из него некоторое подмножество элементов, которое будем называть фрагментом массива. Фрагмент может содержать как последовательные элементы массива, так и просто отдельные элементы массива. Последовательность элементов во фрагменте может быть произвольной. Для выполнения поставленной задачи язык Perl предлагает специальную синтаксическую конструкцию. Если после имени массива в квадратных скобках задать некоторый список целых чисел, представляющих индексы некоторых элементов массива, то такая конструкция и будет означать выделение фрагмента массива, состоящего из указанных элементов исходного массива. Несколько примеров создания фрагментов массива представлены ниже.
@аrrау = ("a".."g");  # Исходный массив:
                      # а, b, с, d, e, f, g
@аrray[0, 3, 5]       # Фрагмент:  a, d, f 
@array[5, 1..2]       # Фрагмент:  f, b, c 
@array[-l, 1..2, -2]  # Фрагмент: g, b, с, f

Стандартные функции обработки массивов скаляров

В языке Perl определен ряд стандартных функций для добавления и удаления элементов из массива скаляров.
Две функции — pop и shift — удаляют из массива скаляров соответственно последний и первый элемент и возвращают его значение. После удаления первого элемента массива функцией shift оставшиеся сдвигаются влево, что уменьшает на единицу индекс, по которому можно ссылаться на эти элементы. Удаление функцией pop последнего элемента массива также приводит к уменьшению на единицу индекса массива. Если переданный в качестве параметра в эти функции массив пуст, то обе функции возвращают неопределенное значение undef.
Эти функции можно использовать вообще без параметра. В этом случае, если они используются в основной программе, то в качестве параметра используется специальный массив @ARGV, что позволяет последовательно получать переданные в сценарий из командной строки параметры в прямом или обратном порядке. Если функции используются в пользовательской подпрограмме, то они работают со специальным массивом @_, с помощью которого передаются фактические параметры в подпрограмму при ее вызове.
Функции push и unshift соответственно добавляют в конец или в начало массива список значений в порядке их задания, возвращая новое количество элементов в массиве скаляров. Обращение к этим функциям требует задания двух фактических параметров: имени массива и списка добавляемых значений элементов.
Удалить определенное количество элементов после элемента с заданным порядковым номером в массиве можно функцией splice со следующим синтаксисом:
splice МАССИВ, НОМЕР [, КОЛИЧЕСТВО];
Если количество элементов не задано, то удаляются все элементы, расположенные в массиве после элемента с указанным порядковым номером, причем так же, как и в случае с функцией shift, индексы оставшихся элементов переформируются в непрерывную последовательность.
ВНИМАНИЕ. В функции splice задается порядковый номер элемента массива, а не его индекс.
Если последним, четвертым параметром задан список скалярных значений, то указанные элементы массива не удаляются, а их значения меняются на значения, определенные в списке.
Фрагмент программы, использующей перечисленные функции, приведен ниже:
@array = ("first", "last");
push @array "a", "b";     # Массив @array содержит:
                          # "first", "last", "a", "b"
splice @array, 2;         # Удаление добавленных элементов
                          # "a" и "b"
unshift @array, "a", "b"; # Массив @array содержит:
                          # "a", "b", "first", "last"
Начиная с версии Perl 5.6.0 удалить элементы из массива скаляров можно функцией delete( ), которая в предыдущих версиях использовалась только для работы с элементами хэш-массивов. Параметром этой функции является элемент или фрагмент массива скаляров, а возвращаемым значением – значение удаленного элемента или список значений удаленных элементов в случае фрагмента массива. Удаление с помощью этой функции возвращает указанные в ее параметре элементы массива (если только они не являются концевыми) в первоначальное, неинициализированное состояние и не производит никаких преобразований индексов оставшихся элементов, то есть удаленные элементы остаются в массиве, но их значения становятся неопределенными undef.
Функцию exists( ) также начиная с версии Perl 5.6.0 можно применять к массивам скаляров для определения существования элемента с указанным индексом. Она возвращает значение «истина», если элементу массива присваивалось значение, даже неопределенное undef, и «ложь» в противном случае, когда элемент массива не инициализирован, удален функцией delete( ) или вообще не существует.
Следующий фрагмент кода Perl-программы демонстрирует применение функций exists( ) и delete( ) к массиву скаляров:
@array = ("first", 2, "third");
@array[9] = 9;
@array[7] = undef;
$ind = 7;
# Печать будет выполнена, элемент инициализирован
print "Элемент $ind существует\n" if exists @array[$ind];
$ind=8;
# Печать будет выполнена, элемент не инициализирован
print "Элемент $ind существует\n" if exists @array[$ind];
delete($array[7]);
# Печать будет выполнена, элемент не инициализирован
print "Элемент $ind существует\n" if exists @array[$ind];
print $#array, "\n"; #Напечатает 9, хотя один элемент
#и удален функцией delete( )
Во многих практических задачах возникает необходимость обработки элементов массива скаляров в обратном порядке, начиная с конца. Можно, конечно, осуществить это с помощью цикла for, в котором переменная индекса уменьшается от максимального значения до минимального, позволяя, таким образом, обработать элементы массива в обратном порядке. Однако ввиду важности задачи обращения массива в Perl существует специальная стандартная функция reverse( ), эффективно выполняющая задачу обращения массива скаляров. Ее единственным параметром являются массив скаляров, а возвращаемым значением – список значений элементов массива в обратном порядке:
@array = (10, 20, 30, 40);
@reverse = reverse(@array); # @reverse =(40,30,20,10)
Задачу сортировки значений элементов массива скаляров можно быстро решить с помощью функции sort( ), передав ей в качестве параметра массив скаляров. Эта функция возвратит отсортированный в лексикографическом порядке список значений массива:
@array = (k, d, c, a);
@sort = sort(@array);
@sort = (a, c, d, k)
Если необходимо отсортировать список в другом порядке, отличном от лексикографического, то следует в списке параметров функции sort( ) перед именем сортируемого массива через пробел (не запятую!) указать имя подпрограммы, определяющей требуемый порядок сортировки, или в фигурных скобках задать оператор сортировки, используя переменную $a для первого значения. Подпрограмма или встроенный оператор сортировки должны возвращать 1, если первое значение больше второго, 0 в случае их равенства и –1, если первое значение меньше второго. Обычно для этих целей используются операции числового <=> и строкового cmp сравнения, которые представлены в главе 4:
# Сортировка в обратном порядке
@sort = sort({$b cmp $a} @array);  # @sort = (k, d, c, a)

Следующая страница Содержание главы


Реклама