Необходимые пояснения к тексту
Для передачи параметров в подпрограмму используется предопределенный массив @_. Встроенная функция shift•() без параметров, вызванная внутри подпрограммы, возвращает первый элемент массива @_ и осуществляет сдвиг всего массива влево, так, что первый элемент пропадает, второй становится первым и т.д. Элемент массива $days[$l] равен суммарному числу дней в первых i месяцах не високосного года, i = (0..11). В переменной $previous_years_days запоминается вычисленное значение общего количества дней, прошедших с 1 января 1 года до начала заданного года.
Обратите внимание на то, что значением функции GetDayO является не название дня недели, а ссылка на анонимную функцию, которая возвращает название дня недели. Объясним, зачем это сделано.
Если бы функция GetDay (} возвращала день недели, то для заполнения календаря на 2000 год, к ней необходимо было бы сделать 366 обращений, вычисляя каждый раз значение переменной $previous_years_days. Для каждого года это значение постоянно, поэтому его достаточно вычислить всего один, а не 366 раз.
На время вычисления функции формируется ее вычислительное окружение, включающее совокупность действующих переменных с их значениями. После завершения вычисления функции ее вычислительное окружение пропадает, и на него невозможно сослаться позже. Часто бывает полезным, чтобы функция для продолжения вычислений могла запомнить свое вычислительное окружение. В нашем примере полезно было бы запомнить значение переменной $previous_years_days, чтобы не вычислять его повторно. В языках программирования существует понятие замыкание,пришедшее из языка Lisp. Это понятие обозначает совокупность, состоящую из самой функции как описания процесса вычислений и ее вычислительного окружения в момент определения функции.
Анонимные процедуры в Perl обладают тем свойством, что по отношению к лексическим переменным, объявленным при помощи функции ту (), выступают в роли замыканий. Иными словами, если определить анонимную функцию в некоторый момент времени при некоторых значениях лексических переменных, то в дальнейшем при вызове этой функции ей будут доступны значения этих лексических переменных, существовавшие на момент ее определения.
В нашем примере указанное свойство анонимных функций используем следующим образом. Чтобы анонимной функцией можно было воспользоваться в дальнейшем, присвоим ссылку на нее скалярной переменной:
$f = GetDay(2000,1,1);
Во время обращения к GetDay о было сформировано вычислительное окружение анонимной функции, на которую сейчас указывает переменная $f. Вычислительное окружение включает, в том числе, и переменную $previous_years_days с ее значением. Обратите внимание, что внутри анонимной функции значение этой переменной не вычисляется. В дальнейшем для заполнения календаря мы будем вызывать анонимную функцию через ссылку $f.
9.5.2. Массив массивов
Сформируем массив @caiendar, используя результаты предыдущего раздела.
for $i (1,3..12) { . '
for $j (1..30) {
$calendar[$i-l][$j-l] =&$f($i, $j);
}
}; • . for $i (1,3,5,7,8,10,12) {
$calendar[$i-l][30] = &$f($i, 31); }; for $j (1..28) {
$calendar[l][$j-l] = &$f(2, $j); };
# Если год високосный, то добавляется еще один элемент массива $calendar[l][28] = &$f(2,29);
Массив @caiendar состоит из 12 элементов по числу месяцев в году. Каждый элемент массива является ссылкой на другой массив, имеющий столько элементов, сколько дней в соответствующем месяце. Значениями элементов вложенных массивов являются английские названия соответствующих дней недели: "Monday", "Tuesday" и т. д.
Обращаем внимание на то, что при формировании массива ^calendar осуществляется неявное создание ссылок $caiendar [$i] и применяется компактная запись $calendar[$i] [$j] для обозначения индивидуального элемента двумерного массива, обсуждавшаяся в разделе 9.3.3.
Содержимое массива @calendar можно вывести для просмотра при помощи следующих операторов:
for $i (0..11) {
for $j (0..$#{$calendar[$i]}) {
print $j+l,".",$i+l," is $calendar[$i][$j]\n";
} };
Напомним, что запись $#array обозначает верхнее значение индекса массива @аггау. В результате выполнения данного цикла будет выведена длинная последовательность строк вида
1.1 is Saturday 2.1 is Sunday
9.5.3. Другие структуры данных
На основе массива Ocaiendar, содержащего календарь на 2000 год, покажем, как можно строить более сложные структуры данных. Структура двумерного массива не очень удобна для представления содержащихся в ней данных в привычном виде настенного календаря. Перегруппируем данные, объединяя их в группы по дням недели. Для этого построим новую структуру, которую для краткости назовем "массив хешей массивов", отдавая себе отчет в том, что такое словосочетание не только далеко не изящно, но и по существу неточно.
Новая структура представляет собой массив @months, состоящий из 12 элементов по числу месяцев в году. Каждый элемент содержит ссылку на анонимный хеш-массив. Каждый вложенный хеш-массив содержит набор ключей, имеющих имена, совпадающие с английскими названиями дней недели: "Monday", "Tuesday" и т. д. Каждому ключу соответствует значение, являющееся, в свою очередь, ссылкой на анонимный массив, содержащий все числа данного месяца, приходящиеся на день недели, соответствующий ключу: все понедельники, все вторники и т. д. Структура массива @months представлена на рис. 9.2.
Рис 9.2. Структура массива @months |
for $i (0..11) { . for $j (0..$#{$calendar[$i.]}> {
push @{$months[$i]{$calendar[$i][$j]}}, $j+l; } };
Замечание | |
Функция push @array, list помещает список list в конец массива garray. |
Первым аргументом встроенной функции push является массив, в который попадают все дни (i+l)-ro месяца, приходящиеся на один и тот же день недели: все понедельники, все вторники и т. д. На этот массив указывает ссылка $months[$i] {"key"}, где ключ "key" принимает значения "Monday", "Tuesday" и т. д. Для обращения к самому массиву ссылку следует разымено-вать, заключив в фигурные скобки: @{$months[$i] ("key"}}. Если вместо ключа "key" подставить нужное значение из $caiendar[$i] [$j], то получим аргумент функции push.
Вновь сформированную структуру удобно использовать для вывода календаря в традиционном виде. Последовательность операторов
for $i (0..11) {
print "month # ", $i+l, "\n";
for $DayName (keys %{$months[$i]}) {
print " ${DayName}: @{$months[$i]{$DayName}}\n";
} };
распечатает календарь в виде
month #1
Monday 3 10 17 24 31
Thursday 6 13 20 27
Wednesday 5 12 19 26
Sunday 2 9 16 23 30
Saturday 1 8 15 22 29
Friday 7 14 21 28
Tuesday 4 11 18 25
Встроенная функция keys %hash возвращает список всех ключей ассоциативного массива %hash. Вывод ключей осуществляется функцией keys в случайном порядке, поэтому дни недели расположены не в естественной последовательности, а случайным образом.
Для вывода ключей в порядке следования дней недели воспользуемся встроенной функцией сортировки
sortSUBNAME LIST
Замечание | |
Функция sort () сортирует список LIST и возвращает отсортированный список значений. По умолчанию используется обычный лексикографический (словарный) порядок сортировки. Его можно изменить при помощи аргумента SUBNAME, представляющего собой имя подпрограммы. Подпрограмма SUBNAME возвращает целое число, определяющее порядок следования элементов списка. Любая процедура сортировки состоит из последовательности сравнений двух величин. Для того чтобы правильно задать порядок сортировки, надо представить себе SUBNAME как функцию двух аргументов. В данном случае аргументы в подпрограмму SUBNAME передаются не общим для Perl способом - через массив @_, а через переменные $а и $ь, обозначающие внутри подпрограммы соответственно первый и второй аргумент. Подпрограмму SUBNAME надо составить таким образом, чтобы она возвращала положительное целое, нуль, отрицательное целое, когда при сравнении аргумент $а назначается меньшим аргумента $ь, равным аргументу $b, большим аргумента $ь соответственно. Для этого внутри подпрограммы удобно использовать операции числового (<=>) и строкового (стр) сравнения, возвращающие значения -1,0, 1, если первый аргумент соответственно меньше второго, равен второму, больше второго. |
Вместо имени подпрограммы в качестве аргумента SUBNAME может использоваться блок, определяющий пррядок сортировки.
Зададим функцию weekOrder, определяющую порядок сортировки
sub WeekOrder {
my %week=("Monday"=>0,
"Tuesday"=>1,
"Wednesday"=>2,
"Thursday"=>3,
"Friday"=>4,
"Saturday"=>5,
"Sunday"=>6) ; $week{$a}<=>$week{$b} };
Используя функцию sort () с заданным порядком сортировки
for $i (0..11) {
print "month # ", $1+1, "\n";
for $DayName (sort WeekOrder keys %{$months[$i]}) { print " $DayName @{$months[$i]{$DayName}}\n";
} • ' ' };
получим структурированный вывод календаря в виде, упорядоченном по месяцам и дням недели:
month f 1
Monday 3 10 17 24 31
Tuesday 4 11 18 25
Wednesday 5 12 19 26
Thursday 6 13 20 27
Friday 7 14 21 28
Saturday 1 8 15 22 29
Sunday 2 9 16 23 30
В качестве следующего примера построим на основе массива gmonths новую структуру, которую можно было бы назвать "хеш-массив хеш-массивов массивов", если бы такое название имело право на существование. В действительности, все просто. Речь идет о том, чтобы заменить в массиве @months числовые индексы ключами, совпадающими с названиями месяцев, и таким образом получить ассоциативный массив %months со сложной внутренней структурой (см. рис. 9.3).
Рис 9.3. Ассоциативный массив %months со сложной внутренней структурой |
При построении хеш-массива %months воспользуемся вспомогательным хеш-массивом %OrderedMonths, который будем использовать для задания порядка сортировки:
# вспомогательный массив %OrderedMonths %OrderedMonths =( "January"=>0,
"February"=>l,
"March"=>2,
"April"=>3,
"Мау"=>4, "June"=>5, "July"=>6, "August"=>7, "September"=>8, "October"=>9, "November"=>10, "December"=>ll ); # формирование структуры for $month (sort {$OrderedMonths{$a}<=>$OrderedMonths{$b}}
keys %OrderedMonths) { $i = $OrderedMonths{$month}; $months{$month}=$months[$ i];' };
# Вывод элементов хеш-массива %months for $month (sort {$OrderedMonths{$a}<=>$OrderedMonths{$b}}
keys %OrderedMonths) { print "$month\n"; $i = $OrderedMonths{$month); for $DayName (sort WeekOrder keys %{$months{$month}}) {
print " $DayName @{$months[$i]{$DayName}}\n"; } };
В результате выполнения примера 9.3 будет распечатан календарь на 2000 год в виде:
January
Monday 3 10 17 24 31
Tuesday 4 11 18 25
Wednesday 5 12 19.26
Thursday 6 13 20 27
Friday 7 14 21 28
Saturday 1 8 15 22 29
Sunday 2 9 16 23 30
Рассмотренные примеры иллюстрируют подход, используемый в Perl для построения сложных структур данных. Читатель может сравнить возможности, предоставляемые языком Perl, с возможностями распространенных языков программирования, таких как Pascal или С. Любая сложная структура в Perl на "верхнем" уровне представляет собой массив или ассоциативный массив, в который вложены ссылки на массивы или хеш-массивы следующего уровня и т. д. В этой иерархии ссылки на массивы и хеш-массивы могут чередоваться в произвольном порядке. При помощи такого подхода средствами Perl можно представить любую структуру С или запись языка Pascal. Perl позволяет с легкостью создавать структуры, которые в других языках создать трудно или невозможно, например, структуру, эквивалентную массиву, состоящему из элементов разных типов:
@аггау = (1, 2 ,3, ("опе"=>1, "two"=>2}, \sfunc, 4, 5};
Читатель может поупражняться в построении таких структур и открыть для себя новые нюансы применения этого гибкого и мощного подхода.
В заключение несколько слов о фрагментах массивов. Для доступа к элементам массива мы имеем специальную нотацию, состоящую из префикса $, имени массива и индекса элемента в квадратных скобках, например, $аггау[7]. Если здесь вместо индекса поместить список индексов, а префикс $ заменить префиксом @, то такая запись будет обозначать фрагмент массива, состоящий из элементов с индексами из заданного списка. Подобную нотацию можно использовать в выражениях, например,
Ssubarrayl = @array[7..12]; @subarray2 = @array[3,5,7];
Массив @subarrayi является фрагментом массива garray, состоящим из элементов со значениями индекса от 7 до 12. Массив @subarray2 является фрагментом массива @аггау, состоящим из элементов со значениями индекса 3, 5 и 7. В первом случае список индексов задан при помощи операции "диапазон", во втором случае - перечислением.
Для многомерного массива понятие "фрагмент" обобщается и означает подмножество элементов, получающееся, если для некоторых индексов из диапазона их изменения выделить список допустимых значений. Для выделения одномерных фрагментов можно воспользоваться приведенной выше нотацией. Например, для выделения из массива @caiendar фрагмента, содержащего календарь на первую неделю апреля, можно использовать запись
@april_first_week = @{'$calendar [3] } [0. . 6];
Если выделяемый фрагмент является многомерным, то для его обозначения специальной нотации не существует. В этом случае следует сформировать новый массив, являющийся фрагментом исходного массива. Например, для выделения из массива @caiendar календаря на первый квартал можно воспользоваться циклом
for $i (0..2) { .
for $j (0..$#{$calendar[$i]}) {
$quarter.l[$i] [$j] = $ calendar [$i] [$j ] ; } ' };
11. Операции с регулярными выражениями: поиск, замена,
Транслитерация.
Основными операциями этого типа являются операция поиска m//, операция замены s/// и операция транслитерации tr///.
Операция поиска
Операция поиска m/PATTERN/ осуществляет поиск образца PATTERN. Общая форма записи этой операции имеет вид:
m/PATTERN/cgimosx
Результатом операции является значение 1 («истина») или пустая строка («ложь»). По умолчанию поиск осуществляется в строке, содержащейся в специальной переменной $_, Можно назначить другую строку для поиска в ней заданного образца при помощи операций связывания =~ или !~:
$var =~ m/PATTERN/cgimosx
В результате последней операции поиск образца PATTERN будет осуществляться в строке, задаваемой переменной $var. Если в правой части операции связывания стоит операция поиска m//, то в левой части не обязательно должна находиться переменная, а может быть любое строковое выражение.
Операция !~ отличается от операции =~ тем, что возвращает противоположное логическое значение. Например, в результате поиска в строке "аaаbbbссс" образца /b+/:
$s = "aaabbbccc" =~ m/b+/;
$s = "aааbbbссс" !~ m/b+/;
в обоих случаях будет найден фрагмент bbb. Но в первом случае возвращаемое значение, сохраненное в переменной $s, будет равно 1 («истина»), а во втором случае — пустой строке («ложь»).
Образец PATTERN может содержать переменные, значения которых подставляются при каждом выполнении поиска по данному образцу.
Флаги cgimosx модифицируют выполнение операции поиска. Флаги imsx имеют тот же смысл, что и в рассмотренном выше случае конструкции расширенного регулярного выражения (?imsx-imsx):
· i — поиск без учета регистра;
· m — строка трактуется как мультистрока, состоящая из нескольких строк, разделенных символом новой строки;
· s — строка трактуется как одна строка, в этом случае метасимволу . соответствует любой одиночный символ, включая символ новой строки;
· х — разрешается использовать в образцах пробелы и комментарии.
Операция замены
Операция замены s/PATTERN/REPLACEMENT/ осуществляет поиск образца PATTERN и, в случае успеха, замену найденного фрагмента текстом REPLACEMENT. В общем виде операция замены может быть записана следующим образом:
s/PATTERN/REPLACEMENT/egimosx
Возвращаемым значением является число сделанных замен или пустая строка («ложь»), если замен не было. По умолчанию поиск и замена осуществляются в специальной переменной $_. Ее можно заменить другой строкой при помощи операций связывания =~ или !~:
$var =~ s/PATTERN/REPLACEMENT/egimosx
Флаг g задает глобальную замену всех фрагментов, удовлетворяющих образцу PATTERN, текстом REPLACEMENT.
Флаг е означает, что заменяющий текст REPLACEMENT следует рассматривать как Perl-выражение, которое следует предварительно вычислить. Например, в результате выполнения сценария
$str = "abaabbaaabbbaaaabbbb";
$result = $str =~ s/(a+b+)/length($1)/ge;
print "result = $result new str = $str\n";
будет выведено число сделанных замен $result и новое значение строки $str, в которой каждый найденный фрагмент, соответствующий образцу [а+b+], заменен числом, равным его длине:
result = 4 new str = 2468
Флаги imosx имеют тот же смысл, что для операции поиска m//.
Дата добавления: 2016-04-23; просмотров: 554;