Глава 9В этой главе:
Оператор next Оператор redo Метки Модификаторы выражений Операции && и || как управляющие структуры Упражнения Управляющие структурыОператор lastВыполняя некоторые из предыдущих упражнений, вы, возможно, иногда думали: "Если бы в моем распоряжении был С-оператор break, все было бы нормально". Даже если такая мысль и не приходила вам в голову — все равно позвольте мне рассказать о Perl-операторе для преждевременного выхода из цикла last.Оператор last обеспечивает выход из самого внутреннего блока цикла, в котором расположен этот оператор*, передавая управление оператору, следующему непосредственно за этим блоком. Например:while (что-то) ( что-то;ЧТО-ТО! ЧТО-ТО; if (условие) {что-то или другое; что-то или другое; last; # выход из цикла while }еще что-то/ еще что-то; } # last передает управление в эту точку программы* Конструкция типа do (} while/until с точки зрения операторов next, last и redo циклом не считается.если условие истинно, то выполняются строки что-то_или_другое, после чего оператор last завершает цикл while.Оператор last учитывает только блоки, образующие цикл, а не блоки, необходимые для построения определенных синтаксических конструкций. Это значит, что блоки для операторов if и else, равно как и для конструкций do {} while/until, "не считаются"; учитываются только блоки, которые образуют циклы for, foreach, while, until и "голые" блоки. ("Голый" блок — это блок, не являющийся частью более крупной конструкции, например цикла, подпрограммы, оператора if/then/else.)Допустим, мы хотим посмотреть, было ли почтовое сообщение, сохраненное в файле, послано пользователем merlyn. Такое сообщение могло выглядеть следующим образом:From: [email protected] (Randal L. Schwartz) Date: Ol-DEC-96 08:16:24 PM -0700 Subject: A sample mail message Here's the body of the mail message. And here is some more. Нам нужно было бы найти в этом сообщении строку, которая начинается словом From:, а затем посмотреть, не содержит ли эта строка регистрационное имя merlyn.Это можно было бы сделать так: while (<STDIN>) ( # читать входные строкиif (/^From: /) ( # начинается ли строка со слова From:? Если да... if (/merlyn/) ( # то сообщение от merlyn!print "Email from Randal! It's about time!\n"; } last; # дальше искать строки From: не нужно, поэтому выйти } # конец цикла "if from:" if (/^5/) ( # пустая строка?last; # если да, больше строки не проверять > } # конец цикла whileНайдя строку, которая начинается со слова From:, мы выходим из цикла, потому что хотим видеть только первую такую строку. Кроме того, поскольку заголовок почтового сообщения заканчивается на первой пустой строке, мы можем выйти и из основного цикла.Оператор nextКак и last, оператор next изменяет последовательность выполнения программы. Отличие между ними состоит в том, что next заставляет программу пропускать оставшуюся часть самого внутреннего блока цикла, не завершая этот цикл*. Используется оператор next следующим образом:while (что-то) ( первая_часть;первая_ часть ;первая_часть; if (условие) {какая-то часть; какая-то_часты next; ) другая_часть ;другая_ ча сть ;# next передает управление в эту точку программы )Если условие истинно, то выполняется какая-то_часть, а другая_часть пропускается. Как и при использовании оператора last, блок оператора if не считается блоком, образующим цикл.Оператор redoТретий способ передачи управления в блоке цикла — оператор redo. Эта конструкция обеспечивает переход в начало текущего блока (без повторного вычисления контрольного выражения):while (условие) {# redo передает управление в эту точку программыЧТО-ТО; ЧТО-ТО; ЧТО-ТО; if {условие) {какие-то действия; какие-то действия; redo; еще_что-то; еще_что-то; еще что-то; } * Если в данном цикле есть оператор continue, который мы не рассматривали, next переходит в начало блока continue, а не в конец блока цикла. Это практически одно и то же.Блок if здесь тоже не учитывается; считаются только циклообразующие блоки.Пользуясь оператором redo, оператором last и "голым" блоком, можно построить бесконечный цикл, образующийся внутри блока:( -...,. . начальные_действия; начальные_действия ;на чалы1ые_действия; if (условие) ( last;} последующк1е_действия;последух1щие_действия; последухщие_действия; redo; } Такая схема годится для while-подобного цикла, некоторая часть которого должна выполняться как инициализационная перед первой проверкой. (В разделе "Модификаторы выражений" мы покажем, как можно использовать оператор if с меньшим числом знаков препинания.)Метки Что делать, если вы хотите выйти из блока, в которым содержится самый внутренний блок, иными словами, выйти сразу из двух вложенных блоков? В С вы могли бы обратиться к оклеветанному всеми оператору goto. В Perl такая хитрость не нужна — здесь по отношению к любому охватывающему блоку можно использовать операторы last, next и redo, присвоив этому блоку имя с помощью метки.Метка — это еще один тип имени из еще одного пространства имен, отвечающего тем же правилам, что и имена скаляров, массивов, хешей и подпрограмм. Мы увидим, однако, что в метке нет специального префиксного символа (аналогичного $ в скалярах, символу & в подпрограммах и т.д.), поэтому метка print, к примеру, конфликтовала бы с зарезервированным словом print, в силу чего ее использование не допускается. По этой причине лучше создавать метки с именами, состоящими только из прописных букв и цифр, которые в будущем никогда не будут использоваться в зарезервированных словах. Кроме того, прописные буквы и цифры хорошо выделяются в тексте программы, который набирается в основном с использованием нижнего регистра.Выбрав имя метки, введите его прямо перед оператором, содержащим блок, и поставьте двоеточие: МЕТКА: while (условие) { оператор;оператор; оператор; if (другое_условие) { last МЕТКА;} ) Мы указали метку как параметр в операторе last. Это дает языку Perl указание выйти из блока с именем метка, а не только из самого внутреннего блока. В данном случае, однако, у нас есть только один циклический блок. Давайте рассмотрим вариант со вложенными циклами:OUTER: for ($i=l; $i <= 10; $i++) { INNER: for ($j=l; $j <= 10; $j++) ( if ($i * $j == 63) ( print "$i times $j is 63!\n"; last OUTER; } if ($j>=$i) ( next OUTER; } } }Этот набор операторов позволяет перебирать все результаты перемножения небольших чисел до тех пор, пока не будет найдена пара, произведение которой равно 63 (7 и 9). После того как эта пара будет найдена, проверка остальных чисел теряет смысл, поэтому первый оператор if обеспечивает выход из обоих циклов for, используя для этого оператор last с меткой. Второй оператор if гарантирует, что большим из двух чисел всегда будет первое, переходя к следующей итерации внешнего цикла, как только поставленное условие перестанет быть истинным. Это значит, что числа будут проверяться при значениях переменных ($i,$j) = (1,1), (2,1), (2,2), (3,1), (3,2), (3,3), (4,1) ИТ.Д.Даже если самый внутренний блок имеет метку, действие операторов last, next и redo без необязательного параметра (метки) все равно распространяется на него. Кроме того, с помощью меток нельзя переходить в блок — можно только выходить из него. Операторы last, next или redo должны находиться внутри блока.Модификаторы выражений В качестве еще одного способа сказать "если это, тогда то" Perl позволяет "прикреплять" к выражению, представляющему собой отдельный оператор, модификатор if.выражение if удравляющее_выражение;В данном случае управляющее_выражение проверяется на истинность (по тем же правилам, что и всегда). Если оно истинно, то вычисляется выражение. Это приблизительно эквивалентно конструкции if (управляющее_выражение) { выражение;} Однако в данном случае дополнительные знаки препинания не нужны, оператор читается справа налево, а выражение должно быть простым (т.е. не может быть блоком операторов). Во многих случаях, однако, такая инвертированная запись оказывается наиболее естественным способом задания условия. Например, вот как можно выйти из цикла при возникновении определенного условия: LINE: while (<STDIN>) ( last LINE if /"From: /; } Видите, насколько проще такая запись? Более того, вы даже можете прочитать ее на нормальном языке: "последняя строка, если она начинается со слова From".Кроме описанной выше, существуют и другие формы с модификаторами: выраж2 unless выраж!; # как unless (выражД) ( выраж2; ) выраж2 while выраж!; # как while (выраж!) ( выраж2; } выраж2 until выраж!; # как until (выраж!) { выраж2; }Во всех этих формах сначала вычисляется выраж!, а затем в зависимости от результата что-то делается или не делается с выраж2. Например, вот как можно найти первую по порядку степень числа 2, которая больше заданного числа:chomp($n = <STDIN>) ; $i = 1; # исходное предположение $i *= 2 until $i > $n; # вычислять, пока не будет найденаКак видите, мы опять добиваемся ясности и убираем лишнее. Эти формы нельзя вкладывать одну в другую: нельзя сказать выражЗ while выраж2 if выраж!. Это объясняется тем, что форма выраж2 if выраж! — уже не выражение, а полноценный оператор; после оператора модификатор ставить нельзя.Операции && и || как управляющие структурыОперации & & и | | в тексте программы похожи на знаки препинания или компоненты выражения. Можно ли считать их управляющими структурами? В Perl возможно почти все, поэтому давайте разберемся, что же здесь имеется в виду.Вам часто приходится сталкиваться с концепцией "если это, тогда то". Мы уже видели две подобные формы: if (это) ( то; } # один способ то if это; # другой способВот третья (хотите верьте, хотите нет — но есть и другие):это && то;Почему она работает? Разве это не операция "логическое И"? Давайте посмотрим, что происходит, когда это принимает значения "истина" или "ложь". • Если это — истина, то значение всего выражения все равно не известно, потому что оно зависит от значения элемента то. Поэтому нужно вычислить то.• Если это — ложь, то все выражение будет ложным и нет никакого смысла смотреть на то. Поскольку вычислять то смысла нет, его можно пропустить.Как раз это и делает Perl — вычисляет то лишь в том случае, если это истинно, что делает данную форму эквивалентной двум предыдущим.Аналогичным образом "логическое ИЛИ" работает, как оператор unless (или модификатор unless). Вы можете заменитьunless (это) ( то; )на это 11 то;Если вы знаете, как использовать эти операции в shell для управления командами условного выполнения, то поймете, что в Perl они работают похоже.Какую же из этих форм использовать? Это зависит от вашего настроения (иногда), от того, насколько велики части выражения, наконец, от того, нужно ли вам заключать выражения в круглые скобки из-за конфликтов приоритетов. Возьмите программы своих коллег и посмотрите, что они делают. В них, вероятно, вы обнаружите влияние каждого из этих факторов. Ларри предлагает ставить самую важную часть выражения первой, чтобы акцентировать на ней внимание. Упражнения Ответы к упражнениям приведены в приложении А .1. Модифицируйте упражнение из предыдущей главы так, чтобы операция повторялась до тех пор, пока в качестве одного из значений не будет введено слово end. (Совет: используйте бесконечный цикл, а затем выполните оператор last, если любое из значений равно end.)2. Перепишите упражнение из главы 4 (суммирование чисел до появления числа 999), используя цикл с выходом из середины. (Совет: используйте "голый" блок с оператором redo в конце, чтобы получить бесконечный цикл, и оператор last в середине, выполняющийся при выполнении определенных условий.) |