Изучаем Perl

       

Прогулка по стране Perl


Наше путешествие по стране Perl мы начнем с небольшой прогулки. В ходе этой прогулки мы ознакомимся с некоторыми возможностями языка Perl на примере небольшого приложения. Здесь приведены лишь очень краткие пояснения; каждая тема гораздо подробнее освещается в соответствующей главе. Тем не менее эта короткая прогулка должна дать вам возможность быстро "почувствовать" этот язык, и вы сможете решить, будете ли вы дочитывать книгу до конца или же отправитесь обратно к своим Usenet-новостям и лыжным склонам.

Программа Hello, World

Давайте рассмотрим небольшую программу, которая делает что-то реальное. Вот ваша базовая программа, которая выводит на экран слова "Hello, World":

^! /usr/bin/perl -w print ("Hello, World\n") ;

Первая строка говорит о том, что это программа написана на языке Perl. Кроме того, первая строка является комментарием; ведь комментарием в Perl, как и во многих интерпретирующих языках программирования, являются все лексемы, стоящие после знака # и до конца текущей строки. Но, в отличие от всех остальных комментариев, включенных в эту программу, комментарий в первой строке особенный: Perl ищет здесь необязательные аргументы. В данном случае использовался ключ -w. Этот очень важный ключ дает Perl указание выдавать дополнительные предупреждающие сообщения о потенциально опасных

конструкциях. Вам следует всегда использовать в своих программах ключ -w.

Вторая строка — это выполняемая часть данной программы. Здесь мы видим функцию print. В данном случае встроенная функция print имеет всего один аргумент, С-подобную текстовую строку. В этой строке комбинация символов \п обозначает символ новой строки. Оператор print завершается точкой с запятой (;). Как и в С, все простые операторы в Perl завершаются точкой с запятой*.

Когда вы вызываете эту программу, ядро запускает интерпретатор Perl, который разбирает всю программу (обе строки, включая первую, т.е. комментарий), а затем выполняет компилированный вариант. Первая и единственная операция — выполнение функции print, которая посылает значения своих аргументов на устройство вывода. По окончании выполнения программы этот Perl-процесс завершается и возвращает родительскому shell код успешного выполнения.


* Точку с запятой можно опустить, если оператор является

последним оператором в блоке или файле либо оператором eval.



Скоро вы увидите Perl-программы, в которых print и другие функции иногда вызываются с круглыми скобками, а иногда — без них. Правило здесь простое: круглые скобки для встроенных функций Perl не являются ни обязательными, ни запрещенными. Их применение может прояснить ситуацию, а может и запутать ее, так что выработайте собственный стиль.

Как задавать вопросы и запоминать результат

Давайте немного усложним пример, ведь приветствие Hello, World — слишком простое и статичное. Сделаем так, чтобы программа называла вас по имени. Для этого нам нужно место для хранения имени, способ задания вопроса об имени и способ получения ответа.

Одно из мест для хранения значений (вроде имени) — скалярная переменная. Для хранения вашего имени в нашей программе мы используем скалярную переменную $name. В главе 2, "Скалярные данные", мы более подробно узнаем о том, что можно хранить в этих переменных и что можно с ними делать. Пока же предположим, что мы можем хранить в скалярной переменной только одно число или строку (последовательность символов).

Программа должна иметь возможность спросить у вас имя. Для этого нам нужен способ выдачи приглашения и способ принятия от вас данных. Предыдущая программа показала нам, как можно приглашать, используя для этого функцию print. Получение же строки с терминала осуществляется с помощью конструкции <stdin>, которая (в нашем случае) получает одну строку введенных данных. Введенные данные мы присваиваем переменной $ name. Это дает нам следующую программу:

print "What is your name? "; # Как ваше имя? $name = <STDIN>;

Значение переменной $name пока содержит завершающий символ новой строки (имя Randai поступает как Randal\n). Чтобы избавиться от этого символа, мы воспользуемся функцией chomp, которая в качестве своего единственного аргумента принимает скалярную переменную и удаляет из ее строкового значения завершающий символ перехода на новую строку (пробельный символ), если он присутствует:



chomp ($name) ;

Теперь нам нужно лишь сказать Hello и указать значение переменной $name, что мы можем сделать так, как в shell, поместив эту переменную в заключенную в кавычки строку:

orint "Hello, $name ! \n";

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

Сложив все вместе, получаем:

•ft! /usr/bin/perl -w print "What is your name? " ; $name = <STDIN>; chomp ($name) ; print "Hello, Sname ' \n" ;



Добавляем возможность выбора



Допустим теперь, что у нас припасено какое-то особое приветствие для пользователя по имени Рэндал, а для остальных — обычное. Для этого нам нужно сравнить имя, которое было введено, со строкой Randal, и, если оно совпадает, сделать что-то особое. Давайте добавим в программу С-подобную ветвь tf-then-eise и операцию сравнения:

#!/usr/bin/perl

print "What is your name? "; $name = <STDIN>; chomp ($name} ; if ($name eq "Randal") {

print "Hello, Randal! How good of you to be here!\n"; } else { print "Hello, $name! \n"; # обычное приветствие

В операции eq сравниваются две строки. Если они равны (т.е. совпадают все символы и длина строк одинакова), результатом будет "истина". (В С и C++ подобной операции нет*).

Оператор if выбирает, какой блок

операторов (заключенных между парными фигурными скобками) выполняется; если выражение дает в результате значение "истина", выполняется первый блок, в противном случае выполняется второй блок.



Секретное слово



Теперь, когда у нас есть имя, давайте сделаем так, чтобы человек, выполняющий эту программу, угадывал секретное слово. У всех, кроме Рэндала, программа будет непрерывно требовать ввести это слово, пока пользователь не наберет слово правильно. Сначала мы приведем текст программы, потом дадим пояснение к ней:

#! /usr/bin/perl -w

$secretword = "llama"; # секретное слово print "What is your name? "; $name = <STDIN>; chomp $name;



if ($name eq "Randal") { print "Hello, Randal! How good of you to be here!\n";

* Для получения аналогичного результата можно использовать стандартную подпрограмму libc. Но это не операция.





}

else {

print "Hello, $name ' \n"; # обычное приветствие print "What is the secret word? "; $guess = <STDIN>; chomp ($guess) ; while ($guess ne $secretword) {

print "Wrong, try again. What is the secret worcl.•' '• ; $guess = <STDIN>; chomp ($guess) ;

Сначала мы задаем секретное слово, помещая его в скалярную переменную $secretword. После приветствия программа спрашивает (посредством вызова еще одной функции print) у пользователя (не Рэндала) его вариант секретного слова. Этот вариант сравнивается с заданным секретным словом в операции ne. Данная операция возвращает значение "истина", если сравниваемые строки не равны (т.е. данная операция противоположна операции eq). Результат сравнения управляет циклом while, который выполняет этот блок операторов до тех пор, пока сравнение дает значение "истина".

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

Несколько секретных слов

Давайте посмотрим, как можно модифицировать эту программу так, чтобы она принимала несколько секретных слов. Используя то, что мы уже видели, можно было бы многократно сравнивать вариант-догадку с рядом правильных ответов, хранящихся в отдельных скалярных переменных. Такой список, однако, было бы трудно корректировать или модифицировать в зависимости от дня недели и даты.

Более эффективное решение — хранить все возможные ответы в структуре данных, которая называется список,

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



@words = ("camel", "llarna", "alpaca");

Имена переменных-массивов начинаются с символа @, что позволяет отличать их от имен скалярных переменных. Существует еще один способ записи этой конструкции так, чтобы не нужно было ставить все эти кавычки — с помощью операции qw ( ) , например:

@words = qw (camel llama alpaca) ;

Это абсолютно то же самое; операция qw работает так, как будто мы взяли в кавычки каждую из трех строк.

Присвоив значения элементам массива, мы можем обращаться к каждому из них по индексной ссылке. Так, $words [0] — это camel, $words [1] — llama, a $words [2] — alpaca. Индекс может быть и выражением, поэтому если мы присвоим $i значение 2, то элементом $words [$i] будет alpaca. (Индексные ссылки начинаются с символа $, а нес @, потому что они обозначают один элемент массива, а не весь массив.) Вернемся к нашему предыдущему примеру:

#! /usr/bin/perl -w @words == qw (camel llama alpaca); print "What is your name? " ; $name = <STDIN>; chomp ($name) ; if ($name eq "Randal") {

print "Hello, Randal! How good of you to be here!\n"; } else {

print "Hello, $name ! \n"; # обычное приветствие print "What is the secret word? "; $guess = <STDIN>; chomp ($guess) ;

$i = 0; # сначала попробуем это слово $correct = "maybe"; # догадка верна или нет? while ($correct eq "maybe") { # продолжаем проверку

if ($words [$i] eq $guess) { # верно?

$correct = "yes"; # да! } elsif ($i < 2) { # смотреть еще слова?

$i==$i+l; #в следующий раз посмотреть следующее слово } else { # больше слов нет, должно быть, неверно print "Wrong, try again. What is the secret word?"; $guess = <STDIN>; chomp ($guess) ;

$i = 0; # вновь начать проверку с первого слова }

} # конец цикла while для неверных слов } # конец цикла "не Рэндал"

Заметьте, что мы используем скалярную переменную $correct для того, чтобы показать, все еще ищем мы правильный пароль или уже нашли его.



В этой программе показан также блок eisif оператора if-then-eise. Такой конструкции нет ни в одном другом языке программирования; это сокращенная запись блока else с новым условием if, но без вложения еще одной пары фигурных скобок. Сравнение набора условий в каскадной цепочке if-eisif-eisif-eisif-eise очень характерно для языка Perl. В нем нет эквивалента оператору switch языка С или оператору case языка Паскаль, но вы можете сами без особых хлопот создать такой оператор. Подробности см. в главе 2 книги Programming Perl и на странице руководства perlsyn(1).



Разные секретные слова для разных пользователей



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

Пользователь Секретное слово
Fred Barney Betty Wilma camel llama alpaca alpaca
Обратите внимание: у последних двух пользователей одинаковые секретные слова. Такое допускается.

Самый простой способ сохранить такую таблицу — использовать хеш. В каждом элементе хеша содержится отдельное скалярное значение (как и в массиве любого другого типа), но в соответствие каждому элементу хеша ставится ключ. Ключом может быть любое скалярное значение (любая строка или число, в том числе нецелые и отрицательные числа). Чтобы создать хеш под именем Swords (обратите внимание на то, что используется символ ^ вместо @) с ключами и значениями, данными в приведенной выше таблице, мы присвоим Swords значение (почти так же, как мы делали раньше с массивом):

Swords = qw( fred camel barney llama betty alpaca wilma alpaca

);

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

Чтобы найти секретное слово для Betty, мы должны использовать имя Betty как ключ в ссылке на хеш Swords с помощью выражения вроде $words { "betty"}. Значение этой ссылки - alpaca, это похоже на то, что мы видели раньше, работая с другим массивом. Как и раньше, ключом может быть любое выражение, поэтому установка $person в значение betty и вычисление $words { $person} также дает в результате alpaca.



Сведя все это воедино, мы получаем такую программу:

#! /usr/bin/perl -w %words = qw ( fred camel barney llama betty alpaca wilma alpaca

) ;

print "What is your name? "; $name = <STDIN>;

chomp ( $name) ; if ($name eq "Randal") {

print "Hello, Randal! How good of you to be here!\n"; } else {

print "Hello, $name !\n"; # обычное приветствие $secretword = $words {$name}; # получить секретное слово print "What is the secret word? "; $guess = <STDIN>; chomp ($guess) ; while ($guess ne $secretword) {

print "Wrong, try again. What is the secret word? "; $guess = <STDIN>; chomp ($guess) ;

} }

Обратите внимание на то, как происходит поиск секретного слова. Если имя не найдено, то значением переменной $secretword будет пустая строка*, и мы можем использовать оператор if, чтобы задать секретное слово по умолчанию для кого-нибудь еще. Вот как это выглядит:

[ . .. остальная часть программы удалена

.
. . ] $secretword = $words ($name}; # получить секретное слово

if ( $secretword eq "") { # не найдено

$secretword = "groucho"; # конечно,

#можно использовать

}

print "What is the secret word? "; [. .. остальная часть программы удалена .. . ]

Обработка различных вариантов ввода секретного елова

Если вместо Randai вы введете Randai L. Schwartz или randal, то тем самым лишите Рэндала права на особое приветствие, потому что сравнение eq предполагает точное равенство. Давайте рассмотрим один способ решения задачи обработки различных вариантов ввода.

* На самом деле это значение undef, но для операции eq оно выглядит как пустая строка. Если бы в командной строке вы использовали ключ -w, то получили бы предупреждение на этот счет. Именно поэтому мы его здесь опустили.


Содержание раздела