Н. Вирт - Программирование на языке Модула-2 (1160777), страница 6
Текст из файла (страница 6)
При каждом повторении должно происходить приближение к цели, т.е. к условию окончания.Очевидным следствием этого шляется необходимость того, чтобы повторяющиеся вычислениявлияли каким-либо образом на условие. Следующие условия либо неверны, либо зависят отнекоторых предварительных условии, которые должны выполняться до начала выполнения цикла.WHILE i > 0 DO20k := 2*k (* i не изменяется *)ENDWHILE l # 0 DOi := i — 2 (* i должно быть четным и положительным *)ENDWHILE n # 1 DOn := n*i; i := i + 1END2. Если условие не выполнено в самом начале, то цикл эквивалентен пустому оператору, т.е. непроизводит никаких действий.3.
Для того чтобы выяснить, каков эффект выполнения цикла, нужно установить соотношение,сохраняющееся при повторениях и называемое инвариантом. В приведенном выше примеределения инвариант - это уравнение q*у + r = х, выполняющееся перед началом каждогоповторения. В примере возведения в степень инвариант - z*x^i = х^k, который вместе с условием1=0 дает желаемый результат z = x^k.4. Следует избегать повторения идентичных вычислений (хотя терпение ЭВМ безгранично и онане будет жаловаться).
Простое правило - избегать внутри повторяющихся операторов выражений,в которых ни одна переменная не меняет своего значения. Например, операторWHILE i < 3*N DOtab[i] := x + y*z + z*i;i := i + 1ENDследует записать более эффективно какn : = 3*N; u : = x + y*z;WHILE 1 < n DOtab[i] := u + z*i; i := i + 1ENDКроме оператора цикла с условием продолжения имеется оператор цикла с условием окончания(ЦиклДо).
Этот цикл выполняется да тех пор, пока условие не станет истинным.$ЦиклДо » "REPEAT" ПослОператоров "UNTIL" Выражение.Существенное отличие его от первого заключается в том, что условие окончания проверяетсявсякий раз после (а не до) выполнения последовательности операторов. В результатепоследовательность всегда выполняется по крайней мере один раз. Преимущество состоит в том,что условие может содержать переменные, значение которых не определено до выполнения цикла.REPEAT21i := i + 50; j := j + 2; k := i DIV jUNTIL k > 23REPEATr:=r-y; q:=q + lUNTIL r < уДва приведенных типа операторов цикла - наиболее распространенные и простые конструкцииповторения.
Но существуют и другие, в особенности оператор цикла с шагом, который будетописан позже в соответствующем месте. Безусловный цикл -обобщение циклов с условиемокончания и условием продолжения, поскольку он позволяет задавать условие окончания вразличных местах повторяющейся последовательности операторов. Его завершениеосуществляется оператором, состоящим из одного ключевого слова EXIT (выход). Хотябезусловный цикл и удобен в некоторых случаях, мы рекомендуем использовать операторы спред-и постусловием, поскольку они более ясно выделяют единственное условие завершения всинтаксически очевидной точке.$БезусловныйЦикл = "LOOP" ПослОператоров "END".6.2.
Условные операторыУсловный оператор имеет вид$$УсловныйОператор * "IF" Выражение"THEN" ПослОператоровS{"ELSIF" Выражение "THEN" ПослОператоров)S["ELSE" ПослОператоров] "END".Следующий пример иллюстрирует общий вид условного оператора.IF R1 THEN S1ELSIF R2 THEN S2ELSIF R3 THEN S3ELSE S4ENDЕго смысл очевиден из значения слов (IF - если; THEN - то: ELSE - иначе: ELSIF - иначе, если ... ).Следует, однако, помнить, что выражения Rl ... R3 вычисляются одно за другим и что, как толькоодно из них даст значение TRUE, будет выполнена сооткетствующая ему последовательностьоператоров, после чего условнный оператор считается завершенным. Последующие условия приэтом не проверяются. Примеры:IF х = 0 THEN s := 0ELSIF x < 0 THEN s := -1ELSE S := 1END22IF ODD(k) THEN Z := z*x ENDIF k > 10 THEN k :- k - 10; d :- 1ELSE d := 0ENDРассмотренные нами структуры уже позволяют разработать носколько простых завершенныхпрограмм, описанных далее.
Первый пример — расширение нашего вводного примера,вычисляющего наибольший общий делитель (нод) двух натуральных чисел х и у. Расширениесостоит во введении переменных u и v и операторов, которые позволяют вычислить наименьшееобщее кратное (нок) для х и у. Величины нок и нод связаны соотношениемнок(х,у)*нод(х,у) = х*уMODULE ноднок:FROM InOut IMPORT ReadCard,WriteLn,WriteString,WriteCard;VAR x,y,u,v: CARDINAL:BEGINWriteString("x="); ReadCard(x); WriteLn;WriteString("y="); ReadCard(y);u := x; v := y;WHILE x # у DO(* нод(х,у) - нод(х0,у0), x*v + y*u = 2*x0*y0 *)IF x > у THENx:=x-y; u:=u + vELSEy:=y-x; v=v+uENDEND;WriteCard(x,6); WriteCard((u + v) D1V 2),6); WriteLnEND ноднок.Это еще один пример вложенных управляющих структур.
Повторение, выраженное оператором спредусловием, включает в себя условную структуру, выраженную условным оператором IF,который в свою очередь включает две последовательности операторов, состоящих каждая из двухприсваивании. Эта иерархическая структура выделена соответствующими сдвигами "внутренних"частей.23Другой пример, демонстрирующий иерархическую структуру,действительного (REAL) числа х, где i -натуральное число.вычисляет1-юстепеньMODULE Степень;FROM InOut IMPORT ReadCard,WriteStrlng,WriteLn;FROM RealInOut IMPORT ReadReal,Done,WriteReal;VAR i: CARDINAL; x,z: REAL;BEGINWriteString("x="); ReadReal(x);WHILE Done DOWriteStrlng("^i="); ReadCard(i);z := 1.0;WHILE 1 > 0 DO(* z * х^i = x0^i0 *)z:= z*x; i := i-1END;WriteReal(z,16); WriteLn;WriteString("x="); ReadReal(x)END;WriteLnEND Степень.Здесь операторы, вычисляющие степень, охвачены еще одной конструкцией повторения: каждыйраз после получения результата запрашивается новая пара х и 1.
Внешнее повторение управляетсялогической переменной Done, указывающей, действительно ли введено число х. (Эта переменнаяимпортируется и ее значение устанавливается процедурой чтения ReadReal).Лобовое вычисление степени многократными умножениями - операция вполне корректная,но не очень экономная. Мы теперь дадим более сложное и более эффективное решение. Онобазируется на следующих соображениях: цель повторения - достигнуть значения i = 0. Этополучается последовательным уменьшением i при сохранении инварианта z*x^i = х0^i0, где х0 иi0 обозначают начальные значения х и i.
Более быстрый алгоритм должен, следовательно,основываться на уменьшении i несколько большими шагами. Приведенное здесь решение делит iпополам. Но это подокно, только если i четно. Следовательно, если i нечетно, его нужноуменьшить на 1. Конечно, каждое изменение i должно сопровождаться коррекцией z с цельюсохранения инварианта.Отметим одну деталь: уменьшение i на 1 не выражается явно, а осуществляетсяпоследующим делением на 2.
Еще отметим, что Функция ODD(i) (нечетный) равна TRUE, если i нечетное число, и равна FALSE в противном случае. Идентификаторы х и z обозначаютдействительные значения в отличие от целых значений. Следовательно, они могут представлять идроби.24MODULE Степень;FROM InOut IMPORT ReadCard,WriteString,WriteLn;FROM RealInOut IMPORT ReadReal,Done,WriteReal;VAR i: CARDINAL; x,z: REAL;BEGINWriteString("x="); ReadReal(x);WHILE Done DOWriteStrlng("^i="); ReadCard(l);z := 1.0;WHILE i > 0 DO(* z * x^i = x0^i0 *)IF ODD(i) THEN z := z*x END;x:= x*x; i := i DIV 2END;WriteReal(z,16); WriteLn;WriteString("x="); ReadReal(x)END;WriteLnEND Степень.Следующий пример программы имеет структуру, почти совпадающую с предыдущейпрограммой.
В этом примере вычисляется логарифм по основанию 2 вещественного числа х,значение которого лежит между 1 и 2. Инвариант совместно с условием завершения (b = 0)определяет желаемый результат сумма = log2(х).MODULE Log2;FROM InOut IMPORT WriteString,WriteLn;FROM RealInOut IMPORT ReadReal,Done,WriteReal;VAR x,а,b,сумма: REAL;BEGINWriteString('x="); ReadReal(x);WHILE Done DO(* 1.0 <= x < 2.0 *)WriteReal(x,15);25а := х; b := 1.0; сумма := 0.0;REPEAT (* 1оg2(х) = сумма + b*1оg2(а) *)а := а*а; b := 0.5*b;IF а >= 2.0 THENсумма := сумма + b; а := 0.5*аENDUNTIL b < 1.0Е-7;WriteReal(сумма, 16); WriteLn;WriteSlring("x="): ReadReal(x)END;WriteInEND Log2.Обычно процедуры вычисления стандартных математических Функций не требуютдетального программирования, поскольку они могут быть получены из набора программ,аналогичного модулям ввода и вывода.
Такой набор не совсем удачно называется библиотекойпрограмм. В следующем примере, опять демонстрирующем использование оператора REPEAT,применим для вычисления косинуса и показательной Функции (ехр) подпрограммы избиблиотеки, называемой MathLib0. Мы получим таблицу значений для затухающих колебаний.Обычно набор стандартных процедур включает Функции sin, cos, ехр, In (натуральный логарифм),sqrt (квадратный корень) и arctan (арктангенс).MODULE Колебания:FROM InOut IMPORT ReadCard,WriteString,WriteLn;FROM RealInOut IMPORT ReadReal,WriteReal;FROM MathLib0 IMPORT exp,cos;CONST dx = 0.19634953; (*Pi/16*)VAR i,n: CARDINAL;x,y,r: REAL;BEGINWriteString("n="): ReadCard(n);WriteString("r="): ReadReal(r); WriteLn;i := 0; x := 0.0;REPEAT x := x + dx; i := i + 1;26у := exp(-r*x)*cos(x);WriteReal(x,15); WriteReal(y,15); WriteLnUNTIL i >= nEND Колебания.7. ЭЛЕМЕНТАРНЫЕ ТИПЫ ДАННЫХМы раньше уже говорили о том, что все переменные должны быть описаны.
Это означает,что их имена указываются в заголовке программы. Кроме введения имени (что дает компиляторувозможность обнаружить и отметить неправильно написанные идентификаторы) описания такжесвязывают с каждой переменной тип данных. Этот тип данных представляет собой статическуюинформацию о переменной, в отличие, например, от ее значения. Эта информация тоже можетспособствовать обнаружению в ошибочной программе таких несоответствий, которые могут бытьобнаружены простым просмотром программы без ее выполнения.Тип переменной определяет множество ее возможных значений и операций, применимых кней. Каждая переменная имеет единственный тип, который можно узнать из ее описания.
Каждаяоперация требует операндов определенного типа и выдает результат тоже определенного типа.Следовательно, из текста программы видно, применима ли данная операция к данной переменной.В программе можно объявлять новые типы данных. Такие сконструированные типыобычно образуются как композиция основных типов. Существует некоторое количество наиболеечасто используемых элементарных типов, называемых стандартными типами, которые являются,основными в языке и не требуют описания.
О них будет рассказано в этом разделе, хотянекоторые из них уже возникали в предшествующих примерах.Фактически тип имеют не только переменные, но и константы, Функции, операнды иоперации (их результаты). В случае констант тип обычно выводим по записи самой константы,другими словами, из ее явного описания.Сначала мы расскажем о стандартных типах Модулы, а затем рассмотрим Форму описанийпеременных и констант.
Другие типы данных и описаний будут изложены в последующихразделах.7.1. Тип INTEGER (целый)Этот тип представляет целые числа, и любому значению типа INTEGER соответствуетнекоторое целое число. Операции, применимые к типу INTEGER, включают основныеарифметические операции+сложить-вычесть*умножитьDIV разделитьMOD остаток от деленияДеление нацело, обозначаемое ключевым словом DIV, дает целую часть частного отделения первого операнда на второй.15 DIV 4 = 3-15 DIV 4 = -32715 DIV (-4) = -3(*Судя по данному примеру, то, как автор понимает целую часть отрицательного числа(операция truncate), не согласуется с описанием этой операции в книге Д.