В программировании часто встречаются задачи, требующие многократного выполнения одной и той же группы операторов программы с разными значениями их операндов. Такие процессы называются циклическими или просто циклами. Группа циклически повторяющихся операторов образует так называемое тело цикла, которое может быть представлено как простым, так и составным выражением. Однократное выполнение тела цикла будем называть итерацией.
Телу цикла в программе всегда предшествует заголовок цикла, содержащий обозначение оператора цикла и выражение, определяющее (прямо или косвенно) количество итераций. Заметим, что тело цикла является операндом оператора цикла, следовательно, заголовок и тело цикла составляют неделимую структурную единицу программы. В дальнейшем, используя термин "оператор цикла", будем иметь в виду и заголовок, и тело цикла.
Для организации циклов во всех системах программирования существуют специализированные операторы циклов, применение которых избавляет программиста от необходимости программировать циклы "вручную". MathCAD поддерживает два вида таких операторов – цикл с предопределениемFor(называемый также циклом со счетчиком) и цикл с предусловиемWhile. Описание структуры этих операторов приведено в таблице 5.
5.4.1 Оператор For
Этот оператор следует использовать в тех случаях, когда количество итераций предопределено, то есть известно заранее.
Заголовок цикла этого оператора (правый операнд) содержит переменную, называемую параметром (или счетчиком) цикла, и список значений этого параметра. Число элементов списка определяет и количество итераций – при выполнении каждой итерации параметр цикла получает очередное значение из списка, заданного в заголовке.
Параметр цикла имеет статус внутренней переменной программы и обладает всеми ее свойствами (описанными в параграфе 5.1.4). Как правило, параметр цикла используется в правой части выражений, входящих в состав тела цикла, хотя формально и не запрещено использование его в левой части выражений (то есть слева от оператора локального определения "f"). При этом следует помнить, что если параметр был изменен в теле цикла, его измененное значение будет действовать лишь до конца текущей итерации, так как перед началом следующей итерации параметр все равно получит очередное значение из списка, указанного в заголовке цикла.
Формально допускается вообще не использовать параметр цикла в выражениях тела цикла – в этом случае список значений параметра не играет никакой роли - существенна лишь длина этого списка, определяющая число (возможно бессмысленных) итераций.
По завершению последней итерации будет выполняться оператор программы, следующий после оператора цикла. При этом переменная, использованная в качестве параметра завершенного цикла, сохраняет значение, которое она имела в последней реально выполненной итерации[*]. Заметим, что это значение не всегда совпадает с последним значением из списка, заданного в заголовке цикла, так как возможен "досрочный" выход из цикла при срабатывании оператора Break, включенного в тело цикла.
Список значений параметра цикла записывается в заголовке цикла после символа "Î", обозначающего принадлежность множеству (этот символ не требуется вводить "вручную" – он будет автоматически отображен при вводе оператора For). MathCAD допускает использование трех форм записи этого списка: прямое перечисление – элементы списка явно указываются через запятую, параметр получает значения из списка в порядке их следования; в стиле ранжированной переменной – элементы списка образуют соответствующий арифметический ряд; массив – элементы списка последовательно получают значения элементов массива в порядке следования их индексов (сначала – столбцы слева направо, затем – строки сверху вниз).
Три программы, приведенные на рисунке 21, иллюстрируют различные варианты использования оператора For.
Программа Fact(n) вычисляет факториал числа n. Оператор цикла в этой программе входит в состав составного выражения, которое, в свою очередь, является операндом условного оператора Otherwise. Параметр цикла k получает значения из целочисленного арифметического ряда.
Программа Ch(V,N,p) обрабатывает входной вектор V, заменяя в нем на значение p те элементы, индексы которых заданы элементами второго входного вектора N. В этом примере список значений параметра цикла i задан множеством элементов вектора N. Заметим, что обе эти программы осуществляют входной контроль данных и блокируют выполнение основного алгоритма в случае, если фактические аргументы программы заданы некорректно.
Программа L(M,z), приведенная в примере в), сопровождается подробными комментариями и не требует пояснений. Эта программа иллюстрирует возможность использования нескольких операторов цикла, один из которых включен в число операторов тела другого. Использование вложенных циклов - типичный прием, используемый для обработки многомерных массивов.
Этот пример может быть рекомендован в качестве образца оформления результатов выполнения последующих лабораторных работ и контрольных заданий.
Рисунок 21 – Примеры программирования циклов For
Рисунок 22 иллюстрирует использование операторов Break и Continue в теле цикла. Как правило, эти операторы сами являются операндами условных операторов If или Otherwise.
Оператор Break ("прервать") прерывает выполнение цикла и передает управление оператору, следующему после прерванного оператора цикла. Заметим, что если оператором Break прерван вложенный цикл, выполнение внешнего цикла будет продолжено.
Оператор Continue ("продолжить") действует иначе – он прерывает только текущую итерацию цикла и передает управление заголовку этого цикла, после чего выполнение цикла продолжается со следующей итерации (если, конечно, прерванная итерация не была последней).
Оператор Break допускается использовать и вне тела цикла. В этом случае прерывается выполнение всей подпрограммы, и возвращается результат вычисления последнего фактически выполненного ее выражения.
Рисунок 22 – Примеры использования операторов Break и Continue
Функция SumN(V) суммирует только те элементы вектора, которые содержат скалярные данные числового типа, а остальные элементы пропускаются. Функция Inverse(V) формирует вектор, элементы которого – значения, обратные значениям соответствующих элементов исходного вектора. При этом, если очередной элемент содержит число "0" или не является скаляром числового типа, цикл прерывается. Заметим, что оператор Break в последнем примере не прерывает работы программы, а передает управление оператору Return, следующему непосредственно после оператора For.
5.4.3 Оператор While
В отличие от оператора For, заголовок оператора While (в переводе - "пока") не содержит явных указаний на количество итераций – он содержит логическое выражение, значение которого автоматически вычисляется перед началом выполнения каждой очередной итерации[†]. Пока это выражение "истинно", итерации цикла будут продолжаться; как только после завершения очередной итерации выражение станет "ложным", следующая итерация цикла выполнена не будет, и управление получит оператор программы, следующий после оператора While.
Очевидно, что если в заголовок цикла поместить тождественно ложное логическое выражение, этот цикл не выполнит ни одной своей итерации, а если это выражение тождественно истинно, цикл будет бесконечным (последнюю ситуацию называют зацикливанием программы). Для того, чтобы избежать подобных ситуаций, в число операндов логического выражения должны быть включены одна или более переменных, изменяющих свои значения в теле цикла таким образом, чтобы цикл был конечным (для предотвращения зацикливания могут использоваться и другие средства – например, принудительный выход из цикла оператором Break).
Примеры использования оператора While приведены на рисунке 23. Приведены три варианта решения одной и той же задачи: каждая из программ F0, F1 и F2возвращает индекс первого из элементов исходного вектора V, превосходящего заданное значение z.
Первая программа (пример а) прибавляет единицу к счетчику k в теле цикла While до тех пор, пока очередной k-й элемент исходного вектора не превысит заданного значения z. После этого цикл завершается, и программа возвращает последнее измененное значение переменной k, которое и является решением задачи. Отметим, что в отличие от цикла For, счетчик k здесь необходимо обрабатывать отдельными операторами: инициализировать (то есть присваивать ему начальное значение) перед оператором цикла и изменять его значение в теле цикла.
Нетрудно заметить, что вариант а) программы имеет существенный недостаток: он не предотвращает зацикливания программы в случае, когда задача не имеет решения, то есть когда параметр z превышает значение самого большого элемента вектора V. В этом примере зацикливания в такой ситуации реально не произойдет – но это заслуга не нашей программы, а системы MathCAD, которая проконтролирует выход индекса вектора V за пределы допустимых значений и выдаст сообщение об ошибке.
Свободным от этого недостатка является вариант б) программы, в котором тело цикла содержит дополнительную проверку допустимости очередного значения индекса и принудительно прерывает цикл оператором Break в соответствующей ситуации с выдачей текстового сообщения.
Возможно, наиболее эффективным вариантом решения этой задачи является вариант в), который вообще не использует оператор While. В этой программе переменная k использована только для соблюдения "чистоты стиля" – чтобы исключить обработку параметра цикла i вне оператора For.
Рисунок 23 – Примеры программирования циклов While