Глава 3В этой главе:
Литеральное представление Переменные Операции над массивами и функции обработки массивов Скалярный и списочный контексты <STDIN> как массив Интерполяция массивов Упражнения Массивы и списочные данныеСписок и массив Список это упорядоченные скалярные данные. Массив -- переменная, которая содержит список. Каждый элемент массива это отдельная скалярная переменная с независимым скалярным значением. Значения в списке упорядочены, т.е. расставлены в определенной последовательности, например от младшего элемента к старшему.Массивы могут иметь любое число элементов. Минимально возможный массив не имеет элементов вообще, тогда как максимально возможный может заполнять всю наличную память. Это еще одно подтверждение принятой в Perl стратегии "отсутствия ненужных ограничений".Литеральное представление Списочный литерал (способ представления значения списка в программе) состоит из значений, отделенных друг от друга запятыми и заключенными в круглые скобки. Эти значения образуют элементы списка. Например: (1,2,3) # массив из трех значений 1, 2 и 3 ("fred",4.5) # два значения "fred" и 4,5Элементы списка не обязательно должны быть константами. Это могут быть выражения, которые вычисляются при каждом использовании литерала. Например: ($а, 17; # два значения: текущее значение переменной $а и 17 ($b+$c,$d+$e) # два значенияПустой список (список, не содержащий элементов) представляется пустой парой круглых скобок: () # пустой список (нуль элементов)Элемент списочного литерала может включать операцию конструктора списка. Это два скалярных значения, разделенных двумя точками. Данная операция создает список значений, начиная с левого скалярного значения и кончая правым скалярным значением, с шагом 1. Например:(1 .. 5) # то же самое, что (1, 2, 3, 4, 5)(1.2 .. 5.2) # то же самое, что (1.2, 2.2, 3.2, 4.2, 5.2)(2 .. 6,10,12) # тоже самое, что (2,3,4,5,6,10,12)($а .. $b) # диапазон, заданный текущими значениями переменных $а и $ЬЕсли правый скаляр меньше левого, то список будет пустым, так как в обратном направлении отсчет вести нельзя. Если последнее значение не соответствует целому числу шагов, то список заканчивается там, где приращение на единицу привело бы к появлению числа, не принадлежащего заданному диапазону: (1.3 .. 6.1) # то же самое, что (1.3,2.3,3.3,4.3,5.3)Списочные литералы с множеством коротких текстовых строк со всеми этими кавычками и запятыми выглядят не очень привлекательно: @а = ("fred","barney","betty","wilma"); # уф-ф!Поэтому предусмотрена функция заключения в кавычки, которая создает список из разделенных пробелами слов, заключенных в круглые скобки*: @а = qw(fred barney betty wilma); # так-то лучше! @а = qw(fred barney betty wilma ); # то же самоеОдно из возможных применений списочного литерала в качестве аргумента функции print, с которой мы уже знакомы. Элементы списка выводятся на экран без промежуточных пробельных символов:print("The answer is ",$a,"\n"); # трехэлементный списочный литералЭтот оператор выводит на экран слова The answer is, затем пробел, значение переменной $а и символ новой строки. Другие способы использования списочных литералов мы рассмотрим позднее.* Как и в функциях сопоставления с образцом, о которых мы узнаем позже, в качестве разделителя здесь вместо круглых скобок можно использовать любой символ, не относящийся к числу пробельных символов, букв и цифр.Переменные Переменная-массив содержит одно значение в виде списка (нуль или более скалярных значений). Имена переменных-массивов похожи на имена скалярных переменных. Единственное отличие состоит в первом символе -это не знак доллара ($), а знак @. Например:@fred # массив-переменная @fred @A_Very_Long_Array_Variable_Name @A_Very_Long_Array_Variable_Name_that_is_differentОтметим здесь, что переменная-массив @fred не имеет никакого отношения к скалярной переменной $fred. Для разных типов переменных Perl предусматривает отдельные пространства имен.Переменная-массив, которой еще не присвоено значение, имеет значение (), т.е. пустой список.Выражение может ссылаться на переменные-массивы в целом, а также проверять и изменять отдельные элементы таких массивов. Операции над массивами и функции обработки массивов Операции над массивами и функции обработки массивов манипулируют целыми массивами. Некоторые из них возвращают список, который затем можно использовать как значение в другой функции обработки массивов или присвоить переменной-массиву. Присваивание Вероятно, самая важная операция, проводимая над массивами - операция присваивания, посредством которой переменной-массиву присваивается значение. Эта операция, как и скалярная операция присваивания, обозначается знаком равенства. Perl определяет тип присваивания (скалярное или для массива), анализируя, какой переменной присваивается значение скалярной или массиву. Например:@fred = (1,2,3); # массив fred получает трехэлементное литеральное значение Qbarney = @fred; # теперь оно копируется в @barneyЕсли переменной-массиву присваивается скалярное значение, оно становится единственным элементом этого массива: @huh =1; #1 автоматически становится списком (1)Имена переменных-массивов могут входить в списочный литерал. При вычислении значений такого списка Perl заменяет имена массивов текущими значениями, например:@fred = qw(one two); @barney = (4,5,@fred,6,7) ; # @barney превращается в (4,5,"one","two",6,7) @barney = (8,@barney); # в начале списка элементов Qbarney ставится 8# и "last" в конце. Qbarney = (@barney,"last") ; # @barney превращается в# (8,4,5,"one","two",6,7,"last") Отметим, что вставленные подобным образом элементы массива находятся на том же уровне иерархии, что и остальная часть литерала: список не может содержать другой список в качестве элемента*. Если списочный литерал содержит только ссылки на переменные (а не выражения), то этот литерал также можно рассматривать как переменную. Другими словами, такой списочный литерал можно использовать в левой части операции присваивания. Каждая скалярная переменная в списочном литерале принимает соответствующее значение из списка, стоящего в правой части операции присваивания. Например: ($а,$Ь,$с) = (1,2,3); # присвоить 1 переменной $а, 2 переменной $Ь,# 3 переменной $с ($a,$b) = ($b,$a); # поменять местами $а и $Ь ($d,@fred) = ($a,$b,$c) # присвоить $d значение $а, a @fred значение ($Ь,$с) ($e,@fred) = @fred; # переместить первый элемент массива @fred# в переменную $е. В результате @fred = ($с),# а $е = $ЬЕсли число присваиваемых элементов не соответствует числу переменных, то лишние значения (стоящие справа от знака равенства) просто отбрасываются, а все лишние переменные (слева от знака равенства) получают значение undef.Переменная-массив, входящая в литеральный список, должна стоять в нем последней, потому что переменные-массивы очень "прожорливы" и поглощают все оставшиеся значения. (Конечно, после нее можно поставить и другие переменные, но всем им будет присвоено значение undef.)Если переменная-массив присваивается скалярной переменной, то присваиваемое число является размером массива, например: @fred = (4,5,6); # инициализация массива @fred $а = @fred; # $а получает значение 3, текущий размер массива @fredРазмер возвращается и в том случае, если имя переменной-массива используется там, где должно быть скалярное значение. (В разделе "Скалярный и списочный контексты" мы увидим, что это называется использованием имени * Хотя ссылка на список и может быть элементом списка, на самом деле это не означает использование списка в качестве элемента другого списка. Впрочем, работает это почти так же, что позволяет создавать многомерные массивы. См. главу 4 книги Programming Perl и man-страницу perllol( I).массива в скалярном контексте.) Например, чтобы получить число на единицу меньшее, чем размер массива, можно использовать @fred-l, так как скалярная операция вычитания требует наличия скаляров в обеих частях. Обратите внимание на следующий пример:$а = @fred; # переменной $а присваивается размер массива @fred ($а) = @fred; # переменной $а присваивается первый элемент @fredПервое присваивание скалярное, и массив @fred рассматривается как скаляр, поэтому значение переменной $а будет равно размеру массива. Второе присваивание для массива (несмотря на то, что требуется всего одно значение), поэтому переменной $а в качестве значения присваивается первый элемент массива @fred, а остальные элементы просто отбрасываются.В результате выполнения присваивания для массива мы получаем значение, представляющее собой список. Это позволяет делать "каскадиро-вание". Например: @fred = (Qbarney = (2,3,4)); # @fred и @barney получают значения (2,3,4) @fred = @barney = (2,3,4); # то же самоеОбращение к элементам массива До сих пор мы рассматривали массив в целом, добавляя и удаляя значения с помощью присваивания для массива. Многие полезные программы так и построены с использованием массивов, но без обращения к их элементам. Perl, однако, предоставляет и традиционный способ обращения к элементам массива по их числовым индексам.Элементы массива нумеруются последовательными целыми числами с шагом 1, начиная с нуля*. Первый элемент массива @fred обозначается как $fred[0]. Обратите внимание: при ссылке на элемент вместо знака @ в имени массива используется знак $ . Это объясняется тем, что обращение к элементу массива идентифицирует скалярную переменную (часть массива), которой в результате может быть присвоено значение или которая используется в выражении, например:@fred = (7,8,9); $b = $fred[0]; # присвоить $Ь значение 7 (первый элемент массива @fred) $fred[0] = 5; # теперь @fred = (5,8,9)Точно так же можно обращаться к другим элементам: $с = $fred[l]; # присвоить $с значение 8$fred[2]++; # инкрементировать третий элемент массива @fred$fred[l] +=4; # прибавить 4 ко второму элементу($fred[0],$fred[l]) = ($fred[l],$fred[0]) ; # * Значение индекса первого элемента можно изменить на что-нибудь другое (например, на "1"). Это, однако, повлечет за собой очень серьезные последствия: может запутать тех, кто будет сопровождать ваш код, и повредить программы, которые вы заимствуете у других. По этой причине настоятельно рекомендуем считать такую возможность отсутствующей.Обращение к списку элементов одного массива (как в последнем примере) называется срезом" и встречается достаточно часто, поэтому для него есть специальное обозначение. @fred[0,l] * то же, что и ($fred[0],$fred[l]) @fred[0,l] = @fred[l,0] # поменять местами первые два элемента @fred[0,l,2] = @fred[l,l,l] # сделать все три элемента такими, как второй @fred[l,2] = (9,10); # заменить последние два значения на 9 и 10Обратите внимание: в этом срезе используется не $, а @. Это вызвано тем, что вы создаете переменную-массив (выбирая для этого часть массива), а не скалярную переменную (обращаясь к одному элементу массива).Срезы работают также с литеральными списками и вообще с любой функцией, которая возвращает список: @who = (qw(fred barney betty wilma))[2,31; # Значения индексов в этих примерах литеральные целые, но индекс может быть и любым выражением, которое возвращает число, используемое затем для выбора соответствующего элемента:@fred (7,8,9) ; $а = 2;$b " $fred[$a); f как $fred[2], или 9$с = $fred[$a-l]; # $c получает значение $fred[l], или 8($с) = (7.8,9)[$а-1]; # то же самое, но с помощью срезаТаким образом, обращение к массивам в Perl-программах может производиться так же, как во многих других языках программирования.Идея использования выражения в качестве индекса применима и к срезам. Следует помнить, однако, что индекс среза список значений, поэтому выражение представляет собой выражение-массив, а не скаляр.Sfred = (7,8,9); f как в предыдущем примере Bbarney =(2,1,0);Obackfred = @fred[@barney]; # то же, что и @fred[2,l,0], или ($fred[2],$fred[l],$fred[0]), или (9,8,7)Если обратиться к элементу, находящемуся за пределами массива (т.е. задав индекс меньше нуля или больше индекса последнего элемента), то возвращается значение undef. Например:@fred = (1,2,3) ; $barney = $fred[7]; # $barney теперь имеет значение undef* Присваивание значение элементу, находящемуся за пределами текущего массива, автоматически расширяет его (с присваиванием всем промежуточным значениям, если таковые имеются, значения undef). Например:@fred = (1,2,3); fred[3] = "hi"; # @fred теперь имеет значение (1,2,3,"hi") $fred[6] = "ho"; # @fred теперь имеет значение# (1,2,3,"hi",undef,undef,"ho") Присваивание значения элементу массива с индексом меньше нуля является грубой ошибкой, потому что происходит такое, скорее всего, из-за Очень Плохого Стиля Программирования. Для получения значения индекса последнего элемента массива @fred можно использовать операцию $#fred. Можно даже задать это значение, чтобы изменить размер массива @fred, но это, как правило, не нужно, потому что размер массива увеличивается и уменьшается автоматически.Использование отрицательного индекса означает, что следует вести обратный отсчет от последнего элемента массива. Так, последний элемент массива можно получить, указав индекс -1. Предпоследний элемент будет иметь индекс -2 и т.д. Например:@fred = ("fred", "wilma", "pebbles", "dino"); print $fred(-l]; # выводит "dino" print $#fred; # выводит 3 print $fred[$#fred]; # выводит "dino"Функции push и popОдним из распространенных вариантов использования массива является создание стека данных, где новые значения вводятся и удаляются с правой стороны списка. Эти операции применяются довольно часто, поэтому для них предусмотрены специальные функции: push(@mylist,$newvalue); # означает Omylist = (@mylist,$newvalue) $oldvalue = pop($mylist); # удаляет последний элемент из @mylistЕсли в функцию pop введен пустой список, она возвращает undef, не выдавая, в соответствии с принятым в Perl этикетом, никакого предупреждающего сообщения.Функция push также принимает список значений, подлежащих помещению в стек. Эти значения вводятся в конец списка. Например:@mylist = (1,2,3); push(@mylist,4,5,6) ; # @mylist = (1,2,3,4,5,6) Отметим, что первый аргумент должен быть именем переменной-масси-ва, потому что для литеральных списков функции push и pop смысла не имеют.Функции shift и unshiftФункции push и pop действуют в "правой" части списка (части со старшими индексами). Функции unshift и shift выполняют соответствующие действия в "левой" части списка (части с младшими индексами). Вот несколько примеров:unshift(@fred,$a); # соответствует Bfred = ($a,@fred);unshift (@fred,$a,$b,$c); # соответствует @fred = ($а,$b,$c,@fred);$х = shift(@fred); # соответствует ($x,@fred) = @fred;# с реальными значениями @fred = (5,6,7) ;unshift(@fred,2,3,4); # @fred теперь имеет значение (2,3,4,5,6,7) $х = shift(@fred) ;# $х получает значение 2, @fred теперь имеет значение (3,4,5,6,7)Как и функция pop, функция shift, если в нее ввести пустую перемен-ную-массив, возвращает значение undef.Функция reverseФункция reverse изменяет порядок следования элементов аргумента на противоположный и возвращает список-результат. Например:@а = (7,8,9) ;@b = reverse(@a); t присваивает @Ь значение (9,8,7) @b == reverse (7,8,9); # делает то же самоеОбратите внимание: список-аргумент не изменяется, так как функция reverse работает с копией. Если вы хотите изменить порядок элементов "на месте", список-аргумент следует затем присвоить той же переменной:@Ь = reverse (@b); t присвоить массиву @Ь его же значения,# но расположить его элементы в обратном порядке Функция sortФункция sort сортирует аргументы так, как будто это отдельные строки, в порядке возрастания их кодов ASCII. Она возвращает отсортированный список, не изменяя оригинал. Например:@х sort("small","medium","large") ;# @х получает значение "large", "medium", "small" @у = (1,2,4,8,16,32,64) ;@у = sort (@y); # @у получает значение 1, 16, 2, 32, 4, 64, 8Отметим, что сортировка чисел производится не по их числовым значениям, а по их строковым представлениям (1,16, 2, 32 и т.д.). Изучив главу 15, вы научитесь выполнять сортировку по числовым значениям, по убыванию, по третьему символу строки и вообще каким угодно методом.Функция chompФункция chomp работает не только со скалярной переменной, но и с массивом. У каждого элемента массива удаляется последний пробельный символ. Это удобно, когда вы, прочитав несколько строк как список отдельных элементов массива, хотите одновременно убрать из всех строк символы новой строки. Например:@stuff = ("hello\n","world\n","happy days") ; chomp(@stuff); # Sstuff теперь имеет значение ("hello","world","happy day")Скалярный и списочный контексты Как видите, каждая операция и функция предназначена для работы с определенной комбинацией скаляров или списков и возвращает скаляр или список. Если операция или функция рассчитывает на получение скалярного операнда, то мы говорим, что операнд или аргумент обрабатывается в скалярном контексте. Аналогичным образом, если операнд или аргумент должен быть списочным значением, мы говорим, что он обрабатывается в списочном контексте. Как правило, это особого значения не имеет, но иногда в разных контекстах можно получить совершенно разные результаты. Например, в списочном контексте @fred возвращает содержимое массива Sfred, а в скалярном размер этого массива. При описании операций и функций мы упоминаем эти тонкости.Скалярное значение, используемое в списочном контексте, превращается в одноэлементный массив. <STDIN> как массивОдна из ранее изученных нами операций, которая в списочном контексте возвращает иное значение, чем в скалярном, <stdin>. Как упоминалось выше, <stdin> в скалярном контексте возвращает следующую введенную строку. В списочном же контексте эта операция возвращает все строки, оставшиеся до конца файла. Каждая строка при этом возвращается как отдельный элемент списка, например:$а = <STDIN>; # читать стандартный ввод в списочном контекстеЕсли пользователь, выполняющий программу, введет три строки и нажмет [Ctrl+D]* (чтобы обозначить конец файла), массив будет состоять из трех элементов. Каждый из них является строкой, заканчивающейся символом новой строки, и соответствует введенной пользователем строке.* В некоторых системах конец файла обозначается нажатием клавиш [Ctrl+Z], а в других эта комбинация служит для приостановки выполняемого процесса.Интерполяция массивов Как и скаляры, значения массивов могут интерполироваться в строку, заключенную в двойные кавычки. Каждый элемент массива заменяется его значением, например: Ofred = ("hello","dolly"); $у = 2;$х = "This is $fred[l]'s place"; # "This is dolly's place" $x = "This is $fred($y-l]'s place"; # To же самоеОтметим, что индексное выражение вычисляется как обычное, как будто оно находится вне строки, т.е. оно предварительно не интерполируется. Если вы хотите поставить после простой ссылки на скалярную переменную литеральную левую квадратную скобку, нужно выделить эту скобку так, чтобы она не считалась частью массива: Ofred = ("hello","dolly"); # присвоить массиву @fred значение для проверки $fred = "right";# мы пытаемся сказать "this is right[1]" $х = "this is $fred[l]"; t неправильно, дает "this is dolly" $x = "this is ${fred}[l]"; t правильно (защищено фигурными скобками) $х = "this is $fred"."[1]"; # правильно (другая строка) $х = "this is $fred\[l]"; t правильно (скобку защищает обратная косая)Аналогичным образом может интерполироваться список значений перемен-ной-массива. Самая простая интерполяция интерполяция всего массива, обозначенного именем (с начальным символом @). В этом случае элементы интерполируются по очереди, с разделением их пробелами, например:@fred = ("а", "bb","ccc",1,2,3) ;$а11 = "Mow for Sfred here!";# $all Можно также выбрать часть массива с помощью среза: @fred = ("а","bb","ccc",1,2,3);$а11 = "Now for @fred[2,3] here!";# $all получает значение "Now for ccc 1 here!" $all = "Now for @fred[@fred[4,5]] here!"; то же самоеОпять-таки, если вы хотите поставить после ссылки на имя массива литеральную левую квадратную скобку, а не индексное выражение, можете использовать любой из описанных выше механизмов. Упражнения Ответы к упражнениям приведены в приложении А. 1. Напишите программу, которая читает список строковых значений, стоящих в отдельных строках, и выводит этот список в обратном порядке. Если вы читаете список на экране, то вам, вероятно, нужно будет выделить конец списка, нажав комбинацию клавиш "конец файла" (в UNIX или Plan 9 это, наверное, [Ctri+D], а в других системах чаще всего [Ctrl+Z]).2. Напишите программу, которая читает число, затем список строковых значений (находящихся в отдельных строках), после чего выводит одну из строк списка в соответствии с указанным числом.3. Напишите программу, которая читает список строк, а затем выбирает и выводит на экран случайную строку из этого списка. Чтобы выбрать случайный элемент массива @somearray, поместите в начало программы функциюsrand; (она инициализирует генератор случайных чисел), а затем используйте rand(@somearray) там, где требуется случайное значение, меньшее размера массива @ some-array. |