Программирование на Free Pascal и Lazarus

Операторы управления

Разбить на страницы
Показывать лекцию целиком

3.1 Основные конструкции алгоритма

Как правило, созданию программы предшествует разработка алгоритма Алгоритм — от algorithmi, algorismus, первоначально латинская транслитерация имени математика аль-Хорезми. . Алгоритм — это чёткое описание последовательности действий, которые необходимо выполнить для того, чтобы при соответствующих исходных данных получить требуемый результат. Одним из способов представления алгоритма является блок-схема. При составлении блок-схемы все этапы решения задачи изображаются с помощью различных геометрических фигур. Эти фигуры называют блоками и, как правило, сопровождают текстом. Последовательность выполнения этапов указывают при помощи стрелок, соединяющих эти блоки. Типичные этапы решения задачи изображаются следующими геометрическими фигурами:

  • блок начала (конца ) (рис. 3.1). Текст внутри блока: "начало" ("конец");
  • (рис 3.1) Блок начала (конца) алгоритма
  • блок ввода (вывода ) данных (рис. 3.2). Текст внутри блока: "ввод" ("вывод" или "печать") и список вводимых (выводимых) переменных;
  • (рис 3.2) Блок ввода (вывода)
  • блок решения, или арифметический (рис. 3.3). Внутри блока записывается действие, вычислительная операция или группа операций;
  • (рис 3.3) Арифметический блок
  • условный блок (рис. 3.4). Логическое условие записывается внутри блока. В результате проверки условия осуществляется выбор одного из возможных путей (ветвей) вычислительного процесса.
  • (рис 3.4) Условный блок

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

    Линейный процесс — это конструкция, представляющая собой последовательное выполнение двух или более блоков (рис. 3.5).

    Разветвляющийся процесс задаёт выполнение одного или другого оператора в зависимости от выполнения условия (рис. 3.6).

    Циклический процесс задаёт многократное выполнение оператора или группы операторов (рис. 3.7).

    (рис 3.5) Линейный процесс (рис 3.6) Разветвляющийся процесс (рис 3.7) Циклический процесс

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

    Рассмотрим операторы языка программирования Free Pascal, реализующие основные конструкции алгоритма.

    3.2 Оператор присваивания

    Оператор присваивания в языке Free Pascal состоит из двух символов: двоеточия и знака равенства. Символы := всегда пишут слитно. Пробелы допускаются перед символом двоеточия и после символа равенства.

    В общем случае оператор присваивания имеет вид:

    имя_переменной := значение;

    где значение — это выражение, переменная, константа или функция. Выполняется оператор так. Сначала вычисляется значение выражения, указанного в правой части оператора, а затем его результат записывается в область памяти (переменную), имя которой указано слева. Например, запись a:=b означает, что переменной а присваивается значение выражения b.

    Типы переменной a и выражения b должны совпадать или быть совместимыми для присваивания, то есть тип, к которому принадлежит b, должен находиться в границах типа переменной а.

    Оператор присваивания, как и любой другой оператор в языке Free Pascal, отделяется от других точкой с запятой.

    3.3 Составной оператор

    Составной оператор — группа операторов, отделённых друг от друга точкой с запятой, начинающихся со служебного слова begin и заканчивающихся служебным словом end:

    begin
    оператор_1;
    ...
    оператор_n
    end;
    

    Транслятор воспринимает составной оператор как один оператор.

    3.4 Условные операторы

    В языке Free Pascal одна из основных конструкций алгоритма, разветвляющийся процесс, реализована двумя условными операторами: if и case. Рассмотрим каждый из них.

    3.4.1 Условный оператор if..then..else

    При решении большинства задач порядок вычислений зависит от определённых условий, например от исходных данных или от промежуточных результатов, полученных на предыдущих шагах программы. Для организации вычислений в зависимости от какого-либо условия в языке Free Pascal используется условный оператор if..then..else, который в общем виде записывается так:

    (рис 3.8) Алгоритм условного оператора if..then..else

    if условие then оператор_1 else оператор_2;

    где if..then..else — зарезервированные слова, условие — выражение логического типа Логическое выражение может принимать одно из двух значений: истина или ложь. , оператор_1 и оператор_2 — любые операторы языка Free Pascal.

    Работа условного оператора организована следующим образом. Сначала вычисляется выражение, записанное в условии. Если оно имеет значение истина (True), то выполняется оператор_1. В противном случае, когда выражение имеет значение ложь (False), оператор_1 игнорируется и управление передаётся оператору_2.

    Алгоритм, который реализован в условном операторе if..then..else, представлен на рис. 3.8.

    Например, чтобы сравнить значения переменных $$x$$ и $$y$$, нужно создать следующий программный код:

    write ( ’ x= ’ ); readln ( x );
    write ( ’ y= ’ ); readln ( y );
    if x=y then
    writeln ( ’значение x равно значению y ’ )
    else writeln ( ’значение x не равно значению y ’ );
    

    Если в задаче требуется, чтобы в зависимости от значения условия выполнялся не один оператор, а несколько, необходимо использовать составной оператор:

    if условие then
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end
    else
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end;
    

    Альтернативная ветвь else в условном операторе может отсутствовать, если в ней нет необходимости:

    if условие then оператор;
    или
    if условие then
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end;
    

    В таком "усечённом" виде условный оператор работает так: оператор (группа операторов) либо выполняется, либо пропускается в зависимости от значения выражения, представляющего условие. Алгоритм этого условного процесса представлен на рис. 3.9.

    Пример применения условного оператора, без альтернативной ветви else может быть таким:

    write ( ’ x= ’ ); readln ( x );
    write ( ’ y= ’ ); readln ( y );
    c : = 0;
    {Значение переменной c изменяется только при условии,}
    {что x не равно y.}
    if ( x<>y ) then c :=x+y;
    {Вывод на экран значения переменной c}
    {выполняется в любом случае.}
    writeln ( ’Значение переменной с= ’, c );
    

    Условные операторы могут быть вложены друг в друга. При вложениях условных операторов всегда действует правило: альтернатива else считается принадлежащей ближайшему if, имеющему ветвь else. Например, в записи он принадлежит оператору if с условием_1.

    (рис 3.9) Алгоритм условного оператора if без альтернативной ветви else
    if условие_1 then
    	if условие_2 then
    		оператор_А
    	else оператор_Б;
    

    оператор_Б относится к условию_2, а в конструкции

    if условие_1 then
    	begin
    		if условие_2 then
    			оператор_А;
    		end
    	else оператор_Б;
    

    Для сравнения переменных в условных выражениях применяют операции отношения : =, <>, <, >, <=, >=. Условные выражения составляют с использованием логических операций and, or и not. В языке Free Pascal приоритет операций отношения меньше, чем у логических операций, поэтому составные части сложного логического выражения заключают в скобки.

    Допустим, нужно проверить, принадлежит ли переменная $$x$$ интервалу $$[a, b]$$. Условный оператор будет иметь вид:

    if ( x>=a ) and ( x<=b ) then...

    Запись

    if x>=a and x<=b then...

    неверна, так как фактически будет вычисляться значение выражения

    x>=(a and x)<=b.

    (рис 3.10) Графическое представление задачи 3.1

    Рассмотрим использование оператора if на примерах В задачах этой главы мы не будем уделять много внимания интерфейсу создаваемых программ, чтобы у читателя была возможность разобраться в алгоритмах и способах их записи на языке Free Pascal. .

    ЗАДАЧА 3.1. Дано вещественное число $$x$$. Для функции, график которой приведён на рис. 3.10, вычислить $$y = f (x)$$.

    Аналитически функцию, представленную на рис. 3.10, можно записать так:

    $$y(x) = \begin{cases} 4, x \le -2, \\ x^2, -2 < x < 1, \\ 1, x \ge 1. \end{cases}$$

    Составим словесный алгоритм решения этой задачи:

  • Начало алгоритма.
  • Ввод числа $$x$$ (аргумент функции).
  • Если значение $$x$$ меньше либо равно -2, то переход к п. 4, иначе переход к п. 5.
  • Вычисление значения функции: $$y = 4$$, переход к п. 8.
  • Если значение $$x$$ больше либо равно 1, то переход к п. 6, иначе переход к п. 7.
  • Вычисление значения функции: $$y = 1$$, переход к п. 8.
  • Вычисление значения функции: $$y = x^2$$.
  • Вывод значений аргумента $$x$$ и функции $$y$$.
  • Конец алгоритма.
  • Блок-схема, соответствующая описанному алгоритму, представлена на рисунке 3.11. Как видно, блок-схема нагляднее и проще для восприятия, чем словесное описание алгоритма. В дальнейшем для описания алгоритма мы часто будем использовать именно блок-схемы.

    (рис 3.11) Блок-схема алгоритма решения задачи 3.1

    Текст программы на языке Free Pascal будет иметь вид:

    var
    	x, y : real;
    begin
    	write ( ’ x= ’ );
    	readln ( x );
    	if x<= -2 then y:=4
    	else if x>=1 then y:=1
    	else y:= sqr ( x );
    	writeln ( ’ x= ’, x : 5 : 2, ’    y= ’, y : 5 : 2 );
    end.
    

    Эту программу можно ввести в текстовом редакторе Geany (или в текстовом редакторе Free Pascal) и запустить на выполнение.

    ЗАДАЧА 3.2. Даны вещественные числа $$x$$ и $$y$$. Определить, принадлежит ли точка с координатами $$(x; y)$$ заштрихованной части плоскости (рис. 3.12).

    Как показано на рис. 3.12, область ограничена линиями $$x = -1, x = 3, y = -2$$ и $$y = 4$$. Значит, точка с координатами $$(x; y)$$ будет принадлежать этой области, если будут выполняться следующие условия: $$x \ge -1, x \le 3, y \ge -2$$ и $$y \le 4$$. Иначе точка лежит за пределами области.

    Блок-схема, описывающая алгоритм решения данной задачи, представлена на рис. 3.13.

    (рис 3.12) Графическое представление задачи 3.2 (рис 3.13) Алгоритм решения задачи 3.2

    Текст программы к задаче 3.2:

    var x, y : real;
    begin
    	write ( ’ x= ’ ); readln ( x );
    	write ( ’ y= ’ ); readln ( y );
    	if ( x>= -1) and ( x<=3) and ( y>= -2) and ( y<=4) then
    		writeln ( ’Точка принадлежит области ’ )
    	else
    		writeln ( ’Точка не принадлежит области ’ );
    end.
    
    ЗАДАЧА 3.3. Написать программу решения квадратного уравнения ax^2+ bx + c = 0.

    Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.

    (рис 3.14) Алгоритм решения квадратного уравнения

    Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — корни квадратного уравнения — либо сообщение о том, что корней нет.

    Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.

    Составим словесный алгоритм решения этой задачи.

  • Начало алгоритма.
  • Ввод числовых значений переменных $$a, b$$ и $$c$$.
  • Вычисление значения дискриминанта $$d$$ по формуле $$d = b^2-4ac$$.
  • Если $$d < 0$$, то переход к п. 5, иначе переход к п. 6.
  • Вывод сообщения "Действительных корней нет" и переход к п. 8.
  • Вычисление корней $$x_1 = \left(-b+\sqrt{d}\right)/(2a)$$ и $$x_2 =\left(-b-\sqrt{d}\right)/(2a)$$.
  • Вывод значений $$x_1$$ и $$x_2$$ на экран.
  • Конец алгоритма.
  • Блок-схема, соответствующая этому описанию, представлена на рис. 3.14.

    Текст программы, которая реализует решение квадратного уравнения:

    {Описание переменных.}
    var a, b, c, d, x1, x2 : real;
    begin
    {Ввод значения коэффициентов квадратного уравнения.}
    writeln ( ’Введите коэффициенты квадратного уравнения ’ );
    readln ( a, b, c );
    {Вычисление дискриминанта.}
    d:=b * b-4*a*c;
    {Если дискриминант отрицателен,}
    if d<0 then
    	{то вывод сообщения, что корней нет,}
    	writeln ( ’Действительных корней нет ’ )
    else
    begin
    	{иначе вычисление корней x1, x2}
    	x1:=(-b+sqrt ( d ) ) / 2 / a;
    	x2:=(-b_sqrt ( d ) ) / ( 2 * a );
    	{и вывод их на экран.}
    	writeln ( ’X1= ’, x1 : 6 : 3, ’ -X2= ’, x2 : 6 : 3 )
    end
    end.
    
    ЗАДАЧА 3.4. Составить программу нахождения действительных и комплексных корней квадратного уравнения $$ax^2 + bx + c = 0$$.

    Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.

    Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — действительные корни квадратного уравнения — либо $$x_1$$ и $$x_2$$ — действительная и мнимая части комплексного числа.

    Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.

    Можно выделить следующие этапы решения задачи:

  • Ввод коэффициентов квадратного уравнения $$a, b$$ и $$c$$.
  • Вычисление дискриминанта $$d$$ по формуле $$d = b^2- 4ac$$.
  • Проверка знака дискриминанта. Если $$d \ge 0$$, то вычисление действительных корней: $$x_1 = \frac{-b+\sqrt{d}}{2a}\text{ и } x_2 = \frac{-b-\sqrt{d}}{2a}$$
  • и вывод их на экран. При отрицательном дискриминанте выводится сообщение о том, что действительных корней нет, и вычисляются комплексные корни Комплексные числа записываются в виде $$a+bi$$, где $$a$$ — действительная часть комплексного числа, $$b$$ — мнимая часть комплексного числа, $$i$$ — мнимая единица $$\sqrt{-1}$$.

    (рис 3.15) Алгоритм решения задачи 3.4 $$\frac{-b}{2a}+i\frac{\sqrt{|d|}}{2a}, \frac{-b}{2a}-i\frac{\sqrt{|d|}}{2a}.$$

    У обоих комплексных корней действительные части одинаковые, а мнимые отличаются знаком. Поэтому можно в переменной x1 хранить действительную часть числа $$-b/(2a)$$, в переменной x2 — модуль мнимой части $$\sqrt{|d|}/(2a)$$, а в качестве корней вывести x1+ix2 и x1-ix2.

    На рис. 3.15 изображена блок-схема решения задачи. Блок 1 предназначен для ввода коэффициентов квадратного уравнения. В блоке 2 осуществляется вычисление дискриминанта. Блок 3 осуществляет проверку знака дискриминанта; если дискриминант отрицателен, то корни комплексные, их расчёт происходит в блоке 4 (действительная часть корня записывается в переменную x1, модуль мнимой — в переменную x2), а вывод — в блоке 5 (первый корень x1+ix2, второй — x1-ix2). Если дискриминант положителен, то вычисляются действительные корни уравнения (блоки 6—7) и выводятся на экран (блок 8).

    Текст программы, реализующей поставленную задачу:

    var a, b, c, d, x1, x2 : real;
    begin
    	writeln ( ’Введите_коэффициенты_квадратного_уравнения ’ );
    	readln ( a, b, c );
    	d:=b * b-4*a*c;
    	if d<0 then
    	begin
    	//Если дискриминант отрицателен, то вывод сообщения,
    	//что действительных корней нет, и вычисление комплексных корней.
    		writeln ( ’Действительных корней нет ’ );
    		{Вычисление действительной части комплексных корней.}
    		x1:=-b /(2 * a );
    		{Вычисление модуля мнимой части комплексных корней.}
    		x2:= sqrt ( abs ( d ) ) / ( 2 * a );
    		writeln ( ’Комплексные корни уравнения  ’,
    		a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
    		{Вывод значений комплексных корней в виде $$x1\pm ix2$$}
    		writeln ( x1 : 1 : 2, ’+i * ( ’, x2 : 1 : 2, ’ ) ’ );
    		writeln ( x1 : 1 : 2, ’- i * ( ’, x2 : 1 : 2, ’ ) ’ );
    	end
    	else
    	begin
    	{иначе вычисление действительных корней x1, x2}
    		x1:=(-b+sqrt ( d ) ) / 2 / a;
    		x2:=(-b_sqrt ( d ) ) / ( 2 * a );
    		{и вывод их на экран.}
    		writeln ( ’Действительные корни уравнения  ’,
    			a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
    		writeln ( ’X1= ’, x1 : 1 : 2, ’ X2= ’, x2 : 1 : 2 )
    	end
    end.
    
    ЗАДАЧА 3.5. Составить программу для решения кубического уравнения $$ax^3 + bx^2 + cx + d = 0$$.

    Кубическое уравнение имеет вид

    $$ax^3 + bx^2 + cx + d = 0$$

    После деления на $$a$$ уравнение (3.1) принимает канонический вид:

    $$x^3 + rx^2 + sx + t = 0$$

    где $$r = b/a, s = c/a, t = d/a$$. В уравнении (3.2) сделаем замену $$x = y - r/3$$ и получим приведённое уравнение:

    $$y^3 + py + q = 0,$$

    где

    $$p = \frac{3s-r^2}{3}, q = \frac{2r^{3}}{27} - \frac{rs}{3}+t.$$

    Число действительных корней приведённого уравнения (3.3) зависит от знака дискриминанта $$D = (p/3)^3+ (q/2)^2$$ приведённого кубического уравнения (табл. 3.1).

    Количество корней кубического уравнения
    Дискриминант Количество действительных корней Количество комплексных корней
    D > 0 1 2
    D < 0 3

    Корни приведённого уравнения могут быть рассчитаны по формулам Кардано:

    $$\begin{aligned} y_{1} = u+v\\ y_{2} = \frac{-{u+v}}{2}+\frac{u-v}{2}i\sqrt{3}\\ y_{3} = \frac{-{u+v}}{2}-\frac{u-v}{2}i\sqrt{3} \end{aligned}$$

    где

    $$u=\sqrt[{3}]{-q/2+\sqrt{D}}, v=\sqrt[{3}]{-q/2-\sqrt{D}}.$$

    При отрицательном дискриминанте уравнение (3.1) имеет три действительных корня, но они будут вычисляться через вспомогательные комплексные величины. Чтобы избавиться от этого, можно воспользоваться формулами:

    $$\begin{aligned} y_1=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}\right),\\ y_2=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{2\pi}{3}\right),\\ y_3=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{4\pi}{3}\right), \end{aligned}$$

    где

    $$\rho =\sqrt{\frac{-p^3}{27}}, \cos(\varphi)=\frac{-q}{2\rho}.$$

    Таким образом, при положительном дискриминанте кубического уравнения (3.3) расчёт корней будем вести по формулам (3.4), а при отрицательном — по формулам (3.5).

    После расчёта корней приведённого уравнения (3.3) по формулам (3.4) или (3.5) необходимо по формулам

    $$x_{k}=y_{k}-\frac{r}{3}, k=1,2,3\ldots$$

    перейти к корням заданного кубического уравнения (3.1).

    Блок-схема решения кубического уравнения представлена на рис. 3.16.

    (рис 3.16) Алгоритм решения кубического уравнения

    Описание блок-схемы. В блоке 1 вводятся коэффициенты кубического уравнения, в блоках 2—3 рассчитываются коэффициенты канонического и приведённого уравнений. Блок 4 предназначен для вычисления дискриминанта. В блоке 5 проверяется знак дискриминанта кубического уравнения. Если он отрицателен, то корни вычисляются по формулам (3.5) (блоки 6—7). При положительном значении дискриминанта расчёт идет по формулам (3.4) (блок 9, 10). Блоки 8 и 11 предназначены для вывода результатов на экран.

    Текст программы с комментариями приведён ниже5 При расчёте величин $$u$$ и $$v$$ в программе предусмотрена проверка значения подкоренного выражения. Если $$\frac{-q}{2}\pm \sqrt{D} > 0$$, то $$u=\sqrt[3]{\frac{-q}{2}+ \sqrt{D}} > 0$$, а $$v=\sqrt[3]{\frac{-q}{2}- \sqrt{D}} > 0$$. Если $$\frac{-q}{2}\pm \sqrt{D} < 0$$, то $$u=\sqrt[3]{\frac{-q}{2}+ \sqrt{D}} > 0$$, а $$v=\sqrt[3]{\frac{-q}{2}- \sqrt{D}} > 0$$. Соответственно, при нулевом значении подкоренного выражения $$u$$ и $$v$$ обращаются в ноль. .

    var
    	a, b, c, d, r, s, t, p, q, ro, f i, x1, x2, x3, u, v, h, g : real;
    begin
    	//Ввод коэффициентов кубического уравнения.
    	write ( ’ a= ’ ); readln ( a );
    	write ( ’ b= ’ ); readln ( b );
    	write ( ’ c= ’ ); readln ( c );
    	write ( ’ d= ’ ); readln ( d );
    	//Расчёт коэффициентов канонического уравнения по 3.2.
    	r :=b/a; s := c /a; t :=d/a;
    	//Вычисление коэффициентов приведённого уравнения 3.3.
    	p :=(3 * s - r * r ) / 3;
    	q:=2* r * r * r /27- r * s/3+ t;
    	//Вычисление дискриминанта кубического уравнения.
    	d :=( p /3) * sqr ( p/3)+ sqr ( q / 2 );
    	//Проверка знака дискриминанта,
    	//ветка then реализует формулы (3.5),
    	//ветка else — формулы 3.4
    	if d<0 then
    	begin
    		ro := sqrt (-p * p * p / 27 );
    		//Следующие два оператора реализуют расчёт угла fi,
    		//сначала вычисляется величина косинуса угла,
    		//затем вычисляется его арккосинус через арктангенс.
    		fi :=-q /(2 * ro );
    		fi := pi /2-arctan ( fi / sqrt (1 - fi * fi ) );
    		//Вычисление действительных корней уравнения x1, x2 и x3
    		x1 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3) - r / 3;
    		x2 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+2 * pi /3) - r / 3;
    		x3 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+4 * pi /3) - r / 3;
    		writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, x2 : 1 : 3, ’ x3= ’, x3 : 1 : 3 );
    end
    else
    begin
    	//Вычисление u и v с проверкой знака
    	// подкоренного выражения.
    	if -q/2+ sqrt ( d)>0 then
    		u:=exp (1/3 * ln (-q/2+ sqrt ( d ) ) )
    	else
    		if -q/2+ sqrt ( d)<0 then
    			u:=-exp (1/3 * ln ( abs(-q/2+ sqrt ( d ) ) ) )
    		else
    			u : = 0;
    	if -q/2- sqrt ( d)>0 then
    		v:=exp (1/3 * ln (-q/2- sqrt ( d ) ) )
    	else
    		if -q/2- sqrt ( d)<0 then
    			v:=-exp (1/3 * ln ( abs(-q/2- sqrt ( d ) ) ) )
    		else
    			v : = 0;
    	//Вычисление действительного корня кубического уравнения.
    	x1:=u+v-r / 3;
    	//Вычисление действительной и
    	// мнимой части комплексных корней.
    	h:=-(u+v)/2 - r / 3;
    	g :=(u - v )/2 * sqrt ( 3 );
    	writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, h : 1 : 3, ’+i*  ’, g : 1 : 3,
    				’ x3= ’, h : 1 : 3, ’ -i*  ’, g : 1 : 3 );
    	end
    end.
    
    ЗАДАЧА 3.6. Заданы коэффициенты a, b и c биквадратного уравнения $$ax^4 + bx^2+ c = 0$$. Найти все его действительные корни.

    Входные данные: a, b, c. Выходные данные: x1, x2, x3, x4.

    Для решения биквадратного уравнения необходимо заменой $$y = x^2$$ привести его к квадратному уравнению $$ay^2 + by + c = 0$$ и решить это уравнение.

    Опишем алгоритм решения этой задачи (рис. 3.17):

  • Ввод коэффициентов биквадратного уравнения $$a, b$$ и $$c$$ (блок 1).
  • Вычисление дискриминанта уравнения $$d$$ (блок 2).
  • Если $$d < 0$$ (блок 3), вывод сообщения, что корней нет (блок 4), а иначе определяются корни соответствующего квадратного уравнения $$y_1$$ и $$y_2$$ (блок 5).
  • Если $$y_1 < 0$$ и $$y_2 < 0$$ (блок 6), то вывод сообщения, что корней нет (блок 7).
  • Если $$y_1 \ge 0$$ и $$y_2 \ge 0$$ (блок 8), то вычисляются четыре корня по формулам $$\pm \sqrt{y_1}, \pm \sqrt{y_2}$$ (блок 9) и выводятся значения корней (блок 10).
  • Если условия 4) и 5) не выполняются, то необходимо проверить знак $$y_1$$. Если $$y_1 > 0$$ (блок 11), то вычисляются два корня по формуле $$\pm \sqrt{y_1}$$ (блок 12), иначе (если $$y_2 > 0$$) вычисляются два корня по формуле $$\pm \sqrt{y_2}$$ (блок 13).
  • Вывод вычисленных значений корней (блок 14).
  • (рис 3.17) Алгоритм решения биквадратного уравнения

    Текст программы на языке Free Pascal с комментариями:

    //Описание переменных:
    //a,b,c - коэффициенты биквадратного уравнения,
    //d - дискриминант,
    //x1,x2,x3,x4 - корни биквадратного уравнения,
    //y1,y2 - корни квадратного уравнения ay^2+by+c=0.
    var
    	a, b, c, d, x1, x2, x3, x4, y1, y2 : real;
    begin
    	//Ввод коэффициентов уравнения.
    	writeln ( ’Введите коэффициенты биквадратного уравнения ’ );
    	readln ( a, b, c );
    	//Вычисление дискриминанта.
    	d:=b * b-4*a*c;
    	//Если он отрицателен,
    	if d<0 then
    		//вывод сообщения "Корней нет"
    		writeln ( ’Корней нет ’ )
    	//Если дискриминант >= 0,
    	else
    	begin
    		//вычисление корней квадратного уравнения.
    		y1:=(-b+sqrt ( d ) ) / 2 / a;
    		y2:=(-b-sqrt ( d ) ) / ( 2 * a );
    		//Если оба корня квадратного уравнения < 0,
    		if ( y1 <0) and ( y2 <0) then
    			//вывод сообщения "Корней нет".
    			writeln ( ’Корней нет ’ )
    		//Если оба корня квадратного уравнения >= 0,
    		else if ( y1>=0) and ( y2>=0) then
    		begin
    			//вычисление четырех корней биквадратного уравнения.
    			x1:= sqrt ( y1 );
    			x2:=-x1;
    			x3:= sqrt ( y2 );
    			x4:=-sqrt ( y2 );
    			//Вывод корней биквадратного уравнения на экран.
    			writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 );
    			writeln ( ’X3= ’, x3 : 6 : 3, ’ X4= ’, x4 : 6 : 3 );
    		end
    		//Если не выполнились оба условия
    		// 1. y1<0 И y2<0
    		// 2. y1>=0 И y2>=0,
    		//то проверяем условие y1>=0
    		else if ( y1>=0) then
    		//Если оно истинно
    		begin
    			x1:= sqrt ( y1 );
    			x2:=-x1;
    			writeln ( ’X1= ’, x1 : 6 : 3, ’   X2= ’, x2 : 6 : 3 );
    		end
    		else
    		//Если условие y1>=0 ложно, то
    		begin
    			x1:= sqrt ( y2 );
    			x2:=-x1;
    			writeln ( ’X1= ’, x1 : 6 : 3, ’    X2= ’, x2 : 6 : 3 );
    		end
    	end
    end.
    

    3.4.2 Оператор варианта case

    Оператор варианта case необходим в тех случаях, когда в зависимости от значений какой-либо переменной надо выполнить те или иные операторы.

    case выражение of
    	значение_1 : оператор_1;
    	значение_2 : оператор_2;
    	...
    	значение_N : оператор_N
    else
    	альтернативный_оператор
    end;
    

    Здесь выражение — переменная перечислимого типа (включая char и boolean), значение_1, значение_2,..., значение_N — это конкретное значение управляющей переменной или выражение, при котором необходимо выполнить соответствующий оператор, игнорируя остальные варианты. Значения в каждом наборе должны быть уникальны, то есть они могут появляться только в одном варианте. Пересечение наборов значений для разных вариантов является ошибкой.

    Оператор работает следующим образом. Вычисляется значение выражения. Затем выполняется оператор, помеченный значением, совпадающим со значением выражения. То есть, если выражение принимает значение_1, то выполняется оператор_1. Если выражение принимает значение_2, то выполняется оператор_2 и так далее. Если выражение не принимает ни одно из значений, то выполняется альтернативный_оператор, расположенный после ключевого слова else.

    Альтернативная ветвь else может отсутствовать, тогда оператор имеет вид:

    case выражение of
    	значение_1 : оператор_1;
    	значение_2 : оператор_2;
    	...
    	значение_N : оператор_N;
    end;
    

    Кроме того, в операторе case допустимо использование составного оператора. Например:

    case выражение of
    	значение_1 : begin оператор_A; оператор_B; end;
    	значение_2 : begin оператор_C; оператор_D; оператор_E; end;
    	...
    	значение_N : оператор_N;
    end;
    

    Рассмотрим применение оператора варианта на следующих примерах.

    ЗАДАЧА 3.7. Вывести на печать название дня недели, соответствующее заданному числу D, при условии, что в месяце 31 день и первое число — понедельник.

    Для решения задачи воспользуемся операцией mod, позволяющей вычислить остаток от деления двух чисел, и условием, что первое число — понедельник. Если в результате остаток от деления (обозначим его R) заданного числа D на семь будет равен единице, то это понедельник, двойке — вторник, тройке — среда и так далее. Следовательно, при построении алгоритма необходимо использовать семь условных операторов, как показано на рис. 3.18.

    Решение задачи станет значительно проще, если при написании программы воспользоваться оператором варианта:

    var d : byte;
    begin
    	write ( ’Введите число D= ’ );
    	readln (D);
    	case D mod 7 of {Вычисляется остаток от деления D на 7.}
    		{В зависимости от полученного значения}
    		{на печать выводится название дня недели}
    		1 : writeln ( ’ПОНЕДЕЛЬНИК ’ );
    		2 : writeln ( ’ВТОРНИК ’ );
    		3 : writeln ( ’СРЕДА ’ );
    		4 : writeln ( ’ЧЕТВЕРГ ’ );
    		5 : writeln ( ’ПЯТНИЦА ’ );
    		6 : writeln ( ’СУББОТА ’ );
    		0 : writeln ( ’ВОСКРЕСЕНЬЕ ’ );
    	end;
    end.
    
    (рис 3.18) Алгоритм решения задачи 3.7

    В предложенной записи оператора варианта отсутствует ветвь else. Это объясняется тем, что переменная R может принимать только одно из указанных значений, т. е. 1, 2, 3, 4, 5, 6 или 0.

    ЗАДАЧА 3.8. По заданному номеру месяца m вывести на печать название времени года.

    Для решения данной задачи необходимо проверить выполнение четырёх условий. Если заданное число m равно 12, 1 или 2, то это зима, если m попадает в диапазон от 3 до 5, то весна; лето определяется принадлежностью числа m диапазону от 6 до 8, и, соответственно, при равенстве переменной m 9, 10 или 11 это осень. Понятно, что область возможных значений переменной m находится в диапазоне от 1 до 12, и если пользователь введет число, не входящее в этот интервал, то появится сообщение об ошибке. Для этого в операторе case программы предусмотрена альтернативная ветка else.

    var m: byte;
    begin
    	write ( ’Введите номер месяца m= ’ ); readln (m);
    	//Проверка области допустимых значений переменной m.
    	case m of
    	//В зависимости от значения m на печать
    	//выводится название времени года.
    	1 2, 1, 2 : writeln ( ’ЗИМА ’ ); }
    	3.. 5 : writeln ( ’ВЕСНА ’ ); }
    	6.. 8 : writeln ( ’ЛЕТО ’ ); }
    	9.. 1 1 : writeln ( ’ОСЕНЬ ’ )}
    	//Если значение переменной m выходит за пределы области
    	//допустимых значений, то выдается сообщение об ошибке.
    	else writeln ( ’ОШИБКА ПРИ ВВОДЕ ! ! ! ’ );
    	end
    end.
    

    3.4.3 Обработка ошибок. Вывод сообщений в среде Lazarus

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

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

    MessageDlg (сообщение, тип_сообщения, [ список_кнопок ], справка );

    где

  • сообщение — текст, который будет отображён в окне сообщения;
  • тип_сообщения — определяет внешний вид окна (табл. 3.2);
  • список_кнопок — константы (перечисляются через запятую), определяющие тип кнопок окна сообщения (табл. 3.3);
  • справка — номер окна справочной системы, которое будет выведено на экран, если нажать F1; параметр равен нулю, если использование справки не предусмотрено.
  • Тип окна сообщения
    Параметр Тип окна сообщения
    mtInformation информационное
    mtWarning предупредительное
    mtError сообщение об ошибке
    mtConfirmation запрос на подтверждение
    mtCustom обычное
    Тип кнопки в окне сообщения
    Константа Кнопка
    mbYes Да
    mbNo Нет
    mbOk Ок
    mbCancel Отмена
    mbAbort Прервать
    mbRetry Повторить
    mbIgnore Пропустить
    mbHelp Помощь
    (рис 3.19) Конструирование формы для решения квадратного уравнения

    Вернёмся к задаче решения квадратного уравнения (задача 3.3). Нами был рассмотрен алгоритм решения этой задачи и написана программа на языке программирования Free Pascal. Реализуем эту задачу в среде Lazarus.

    Создадим новый проект Подробно о создании проекта см. в главе 1. (рис. 3.19).

    Для организации ввода коэффициентов уравнения внедрим на форму четыре объекта типа надпись (Label1, Label2, Label3, Label4) и три поля ввода (Edit1, Edit2, Edit3). Корни уравнения или сообщение об их отсутствии будем выводить в надпись Label5 На этапе конструирования формы сделаем метку Label5 невидимой, для чего свойство Visible установим в false (Label5.Visible:=false). . Все действия по вычислению корней квадратного уравнения будут выполняться при нажатии кнопки Button1.

    При вводе данных в программе могут возникнуть следующие ошибки:

  • в поле ввода оказалась строка, которую невозможно преобразовать в число;
  • значение коэффициента a равно нулю При вычислении корней произойдёт деление на ноль. .
  • Для того чтобы не допустить подобные ошибки, необходимо контролировать данные, вводимые пользователем. Применим для этой цели встроенную процедуру Val(S,X,Kod), которая преобразовывает строку S в число. Тип этого числа (целое или вещественное) зависит от типа переменной X. Если преобразование прошло успешно, то параметр Kod принимает значение, равное нулю, а результат преобразования записывается в переменную X. В противном случае Kod содержит номер позиции в строке S, где произошла ошибка, и содержимое переменной X не меняется. Далее приведён фрагмент программы с подробными комментариями:

    procedure TForm1. Button1Click ( Sender : TObject );
    var
    	a, b, c, d, x1, x2 : real;
    	kod1, kod2, kod3 : integer;
    begin
    	//Ввод значений коэффициентов квадратного уравнения.
    	//Из поля ввода Edit1 считывается строка символов и
    	//преобразовывается в вещественное число,
    	//если преобразование прошло успешно, то kod1=0 и
    	//полученное число записывается в переменную a.
    	val ( Edit1. Text, a, kod1 );
    	val ( Edit2. Text, b, kod2 );
    	val ( Edit3. Text, c, kod3 );
    	//Если преобразования прошли успешно, то
    	if ( kod1=0) and ( kod2=0) and ( kod3=0) then
    		//проверить чему равен первый коэффициент.
    		//Если значение первого коэффициента равно нулю, то
    			if a=0 then //выдать соответствующее сообщение.
    				MessageDlg ( ’Введите не нулевое значение а ’,
    					mtInformation, [ mbOk], 0 )
    		else //иначе перейти к решению квадратного уравнения
    		begin
    			d:=b * b-4*a*c;
    			Label5. Visible := true;
    			if d<0 then
    				Label5. Caption := ’В уравнении ’
    					+chr (13)+ ’нет действительных корней ’
    			else
    			begin
    				x1:=(-b+sqrt ( d ) ) / 2 / a;
    				x2:=(-b-sqrt ( d ) ) / ( 2 * a );
    				Label5. Caption := ’X1= ’+FloatToStr ( x1 )
    					+chr (13)+ ’X2= ’+FloatToStr ( x2 );
    			end;
    		end
    	else //Преобразование не выполнено, выдать сообщение.
    	MessageDlg ( ’Введите числовое значение ’,
    			mtInformation, [ mbOk ], 0 );
    end.
    
    (рис 3.20) Обработка ошибки ввода данных — коэффициент a равен 0

    Решение квадратного уравнения $$3x^2+4x+5=0$$

    (рис 3.21) Решение квадратного уравнения(корней нет) (рис 3.22) Обработка ошибки ввода данных — в поле ввода строка, которую невозможно преобразовать в число (рис 3.23) Вычисление корней квадратного уравнения

    Результаты работы программы показаны на рис. 3.20,рис. 3.21,рис. 3.22,3.23.

    Метод обработки ошибок ввода, изложенный в этом примере, не единственный. Можно попытаться контролировать символы, поступающие в поля ввода. Набор символов, которые могут быть преобразованы в число, невелик. Это все цифры от 0 до 9, знак минус и символ запятой. Кроме того, пользователь должен иметь возможность удалять символы из поля ввода. Для этого ему понадобится клавиша BackSpace (#8 — символ с кодом восемь). Представим набор перечисленных символов в виде множества, и если окажется, что введённый символ не принадлежит ему, то будем выводить сообщение об ошибке при вводе.

    Возможность контролировать ввод символов обеспечивает событие OnKeyPress. Если выделить компонент Edit2 и на вкладке инспектора объектов дважды щёлкнуть возле события нажатия клавиши OnKeyPress, то будет создана процедура обработки этого события. Текст процедуры с комментариями:

    //Обработка события ввод символа в поле Edit2.
    procedure TForm1. Edit2KeyPress ( Sender : TObject;
    		var Key : Char );
    begin //Если символ не принадлежит
    		//множеству допустимых символов,то
    	if not ( Key in [#8, ’, ’, ’_ ’, ’ 0 ’.. ’ 9 ’ ] ) then
    	begin //выдать сообщение и
    		MessageDlg ( ’Введите числовое значение ’,
    			mtInformation, [ mbOk ], 0 );
    		Abort; //прервать выполнение подпрограммы.
    end; end.
    

    3.5 Операторы цикла

    Циклический процесс, или просто цикл — это повторение одних и тех же действий. Последовательность действий, которые повторяются в цикле, называют телом цикла. Один проход цикла называют шагом, или итерацией Понятие итерации в математике и программировании несколько отличаются. В математике под итерацией понимают повторение какой-либо математической операции, использующее результат предыдущей аналогичной операции. В программировании итерация — это организация обработки данных, при которой действия повторяются многократно, не приводя при этом к вызовам самих себя (http://ru.wikipedia.org/wiki/Итерация ). . Переменные, которые изменяются внутри цикла и влияют на его окончание, называются параметрами цикла.

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

    В языке Free Pascal для удобства программиста предусмотрены три оператора, реализующих циклический процесс: while, repeat..until и for.

    3.5.1 Оператор цикла с предусловием while..do

    На рис. 3.24 изображена блок-схема алгоритма цикла с предусловием.

    Оператор, реализующий этот алгоритм, в языке Free Pascal имеет вид:

    while выражение do оператор;

    (рис 3.24) Алгоритм циклической структуры с предусловием

    Выражение должно быть логической константой, переменной или логическим выражением.

    Работает оператор while следующим образом. Вычисляется значение выражения.

    Если оно истинно (True), выполняется оператор. Выполнение цикла заканчивается, если условие ложно, после этого управление передается оператору, следующему за телом цикла. Выражение вычисляется перед каждой итерацией цикла. Если при первой проверке выражение ложно (False), цикл не выполнится ни разу.

    Если в цикле надо выполнить более одного оператора, необходимо использовать составной оператор:

    while условие do
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end;
    

    Рассмотрим пример. Пусть необходимо вывести на экран значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0;\pi]$$ с шагом 0.1.

    Применим цикл с предусловием :

    var x, y : real;
    begin
    	{Присваивание параметру цикла стартового значения.}
    	x : = 0;
    	{Цикл с предусловием.}
    	while x<=pi do {Пока параметр цикла не превышает}
    		{конечное значение, выполнять тело цикла.}
    	begin
    		{Вычислить значение y.}
    		y:=exp ( sin ( x ) ) * cos ( x );
    		{Вывод на экран пары х и y.}
    		writeln ( ’ x= ’, x, ’    y= ’, y );
    		{Изменение параметра цикла - переход к следующему значению x.}
    		x:=x + 0.1;
    	end; {Конец цикла.}
    end.
    

    В результате работы данного фрагмента программы на экран последовательно будут выводиться сообщения со значениями переменных x и y:

    x= 0; y=1
    x= 0.1; y =1.0995
    ...
    x= 3.1; y= -1.0415
    

    3.5.2 Оператор цикла с постусловием repeat..until

    Если в цикле с предусловием проверка условия осуществляется до тела цикла, то в цикле с постусловием условие проверяется после тела цикла (см. рис. 3.25). Сначала выполняются операторы, являющиеся телом цикла, после чего проверятся условие, если последнее ложно, то цикл повторяется. Выполнение цикла прекратится, если условие станет истинным.

    (рис 3.25) Алгоритм циклической структуры с постусловием

    В языке Free Pascal цикл с постусловием реализован конструкцией

    repeat
    	оператор;
    	until выражение;
    

    или, если тело цикла состоит более чем из одного оператора

    repeat
    	оператор_1;
    	оператор_2;
    	...
    	оператор_N;
    until выражение;
    

    Выражение должно быть логическим.

    Работает цикл следующим образом. В начале выполняется оператор, представляющий собой тело цикла. Затем вычисляется значение выражения. Если оно ложно (False), оператор тела цикла выполняется ещё раз. В противном случае цикл завершается, и управление передается оператору, следующему за циклом.

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

    Если применить цикл с постусловием для создания подпрограммы, которая выводит значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1, получим:

    var x, y : real;
    	begin
    	{Присваивание параметру цикла стартового значения.}
    	x : = 0;
    	{Цикл с постусловием.}
    	repeat {Начало цикла}
    		y:=exp ( sin ( x ) ) * cos ( x );
    		writeln ( ’ x= ’, x, ’   y= ’, y );
    		x:=x + 0.1; {Изменение значения параметра цикла.}
    	until x > pi; {Закончить работу цикла,}
    		{когда параметр превысит конечное значение.}
    	end.
    

    3.5.3 Оператор цикла for..do

    Операторы цикла с условием обладают значительной гибкостью, но не слишком удобны для организации "строгих" циклов, которые должны быть выполнены заданное число раз. Оператор цикла for..do используется именно в таких случаях:

    for параметр_цикла:=начальное_значение to конечное_значение do
    	оператор;
    for параметр_цикла:=конечное_значение downto начальное_значение do
    	оператор;
    

    где оператор — любой оператор языка, параметр_цикла — имя переменной целочисленного или перечислимого типов, начальное_значение и конечное_значение должны быть того же типа, что и параметр_цикла.

    Шаг изменения цикла for всегда постоянен и равен интервалу между двумя ближайшими значениями типа параметра цикла (при целочисленном значении параметра цикла шаг равен 1).

    В случае если тело цикла состоит более чем из одного оператора, необходимо использовать составной оператор:

    (рис 3.26) Алгоритм работы цикла for..do (рис 3.27) Представление цикла for...do с помощью блок-схемы
    for параметр_цикла:=начальное_значение to конечное_значение do
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_N;
    	end;
    

    Опишем алгоритм работы цикла for..do (рис. 3.26).

  • Параметру_цикла присваивается начальное_значение.
  • Если значение параметра_цикла превосходит конечное_значение, то цикл завершает свою работу. В противном случае выполняется п. 3.
  • Выполняется оператор.
  • Значение параметра_цикла изменяется на соответствующий шаг и осуществляется переход к п. 2, и т. д.
  • Понятно, что этот алгоритм представляет собой цикл с предусловием.

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

    Фрагмент подпрограммы, приведённый далее, демонстрирует применение цикла for:

    var i : integer; c : char;
    	begin
    		{Вывод на экран чисел от 1 до 10.}
    		for i :=1 to 10 do
    			writeln ( i );
    		{Вывод на экран чисел от 10 до -10.}
    		for i :=10 downto -10 do
    			writeln ( i );
    		{Вывод на экран символов от a до r.}
    		for c := ’ a ’ to ’ r ’ do
    			writeln ( c );
    	end.
    

    Вернёмся к задаче вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1. Как видим, здесь количество повторений цикла явно не задано. Однако это значение, можно легко вычислить. Предположим, что параметр цикла $$x$$ принимает значения в диапазоне от $$x_n$$ до $$x_k$$, изменяясь с шагом $$dx$$, тогда количество повторений тела цикла можно определить по формуле:

    $$n = \round\left(\frac{x_k-x_n}{dx}\right)+1$$

    округлив результат деления до целого числа. Следовательно, фрагмент программы вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1 будет иметь вид:

    var i, n : integer; x, y : real;
    	begin
    	n:=round ( ( pi - 0)/0.1)+1; {Количество повторений цикла.}
    	x : = 0; {Начальное значение аргумента.}
    	{Цикл с известным числом повторений,}
    	{i - параметр цикла, изменяется от 1 до n с шагом 1.}
    	for i :=1 to n do
    	begin {Начало цикла.}
    		y:=exp ( sin ( x ) ) * cos ( x ); {Вычисление значения функции }
    		{для соответствующего значения аргумента.}
    		writeln ( ’ x= ’, x, ’    y= ’, y );
    		x:=x + 0.1; {Вычисление нового}
    		{значения аргумента.}
    	end; {Конец цикла.}
    end.
    

    3.5.4 Операторы передачи управления

    Операторы передачи управления принудительно изменяют порядок выполнения команд. В языке Free Pascal таких операторов пять: goto, break, continue, exit и halt.

    Оператор goto метка, где метка — обычный идентификатор, применяют для безусловного перехода, он передаёт управление оператору с меткой.

    метка : оператор;

    Обычно применение оператора goto приводит к усложнению программы и затрудняет отладку. Использование оператора нарушает принцип структурного программирования Как говорил учитель программирования авторов книги Владимир Андреевич Реуцкий: "Использование goto — признак дурного тона". , согласно которому все блоки, составляющие программу, должны иметь только один вход и один выход. В большинстве алгоритмов применения оператора goto можно избежать, в нашей книге мы будем его использовать, но читатель должен знать о наличии этого оператора в языке.

    Операторы break и continue используют только внутри циклов. Оператор break осуществляет немедленный выход из циклов repeat, while, for, и управление передается оператору, находящемуся непосредственно за циклом. Оператор continue начинает новую итерацию цикла, даже если предыдущая не была завершена.

    Оператор exit осуществляет выход из подпрограммы.

    Оператор halt прекращает выполнение программы.

    3.5.5 Решение задач с использованием циклов

    Рассмотрим использование циклических операторов на конкретных примерах.

    ЗАДАЧА 3.9. Найти наибольший общий делитель (НОД) двух натуральных чисел $$A$$ и $$B$$.

    Входные данные: A и B. Выходные данные: А — НОД.

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

    В блок-схеме решения задачи, представленной на рис. 3.28, для решения поставленной задачи используется цикл с предусловием, то есть тело цикла повторяется до тех пор, пока А не равно В. Следовательно, при создании программы воспользуемся циклом while..do.

    Поиск НОД для чисел A = 25 и B = 15.
    Исходные данные Первый шаг Второй шаг Третий шаг НОД(A,B)=5
    А=25 А=10 А=10 А=5
    В=15 В=15 В=5 В=5
    (рис 3.28) Алгоритм поиска наибольшего общего делителя двух чисел

    Программа на языке Free Pascal, реализующая поставленную задачу:

    var a, b : word;
    	begin
    	writeln ( ’введите два натуральных числа ’ );
    	write ( ’A= ’ ); readln ( a );
    	write ( ’B= ’ ); readln ( b );
    	{Если числа не равны, выполнять тело цикла.}
    	while a<>b do
    	{Если число А больше, чем В, то уменьшить его значение на В,}
    	if a>b then
    		a:=a-b
    	{иначе уменьшить значение числа В на А.}
    	else
    		b:=b-a;
    	writeln ( ’НОД= ’,A);
    end.
    
    (рис 3.29) Алгоритм вычисления факториала ЗАДАЧА 3.10. Вычислить факториал числа $$N(N!=1 \cdot 2 \cdot 3 \cdot \ldots \cdot N).$$.

    Входные данные: N — целое число, факториал которого необходимо вычислить.

    Выходные данные: factorial — значение факториала числа N, произведение чисел от 1 до N, целое число.

    Промежуточные переменные: i — параметр цикла, целочисленная переменная, последовательно принимающая значения 2, 3, 4 и так далее до N.

    Блок-схема приведена на рис. 3.29.

    Итак, вводится число N. Переменной factorial, предназначенной для хранения значения произведения последовательности чисел, присваивается начальное значение, равное единице. Затем организуется цикл, параметром которого выступает переменная i. Если значение параметра цикла меньше или равно N, то выполняется оператор тела цикла, в котором из участка памяти с именем factorial считывается предыдущее значение произведения, умножается на текущее значение параметра цикла, а результат снова помещается в участок памяти с именем factorial. Когда параметр i становится больше N, цикл заканчивается, и на печать выводится значение переменой factorial, которая была вычислена в теле цикла.

    Ниже представлен текст программы вычисления факториала на языке Free Pascal.

    var
    	factorial, n, i : integer;
    	begin
    		write ( ’ n= ’ ); readln ( n );
    		factorial : = 1;
    		for i :=2 to n do
    			factorial := factorial * i;
    		writeln ( factorial );
    	end.
    
    ЗАДАЧА 3.11. Вычислить a^n, где n — целое положительное число.

    Входные данные: a — вещественное число, которое необходимо возвести в целую положительную степень n. Выходные данные: p (вещественное число) — результат возведения вещественного числа a в целую положительную степень n. Промежуточные данные: i — целочисленная переменная, принимающая значения от 1 до n с шагом 1, параметр цикла. Блок-схема приведена на рис. 3.30.

    (рис 3.30) Алгоритм возведения вещественного числа в целую степень

    Известно, что для того, чтобы получить целую степень n числа a, нужно умножить его само на себя n раз. Результат этого умножения будет храниться в участке памяти с именем p. При выполнении очередного цикла из этого участка предыдущее значение будет считываться, умножаться на основание степени a и снова записываться в участок памяти p. Цикл выполняется n раз.

    В таблице 3.5 отображён протокол выполнения алгоритма при возведении числа 2 в пятую степень: a=2, n=5. Подобные таблицы, заполненные вручную, используются для тестирования — проверки всех этапов работы программы.

    Процесс возведения числа a в степень n
    i 1 2 3 4 5
    P 1 2 4 8 16 32

    Далее приведён текст программы, составленной для решения поставленной задачи.

    var
    	a, p : real;
    	i, n : word;
    	begin
    		write ( ’Введите основание степени a= ’ );
    		readln ( a );
    		write ( ’Введите показатель степени n= ’ );
    		readln ( n );
    		p : = 1;
    		for i :=1 to n do
    			p:=p * a;
    		writeln ( ’P= ’,P : 1 : 3 );
    	end.
    
    ЗАДАЧА 3.12. Вычислить сумму натуральных четных чисел, не превышающих $$N$$.

    Входные данные: N — целое число.

    Выходные данные: S — сумма четных чисел.

    Промежуточные переменные: i — параметр цикла, принимает значения 2, 4, 6, 8 и так далее, также имеет целочисленное значение.

    При сложении нескольких чисел необходимо накапливать результат в определённом участке памяти, каждый раз считывая из этого участка предыдущее значение суммы и прибавляя к нему следующее слагаемое. Для выполнения первого оператора накапливания суммы из участка памяти необходимо взять такое число, которое не влияло бы на результат сложения. Перед началом цикла переменной, предназначенной для накапливания суммы, необходимо присвоить значение нуль (s=0). Блок-схема решения этой задачи представлена на рис. 3.31.

    Так как параметр цикла i изменяется с шагом 2, в блок-схеме, построенной для решения данной задачи (рис. 3.31), использован цикл с предусловием, который реализуется при составлении программы с помощью оператора while..do:

    (рис 3.31) Алгоритм вычисления суммы чётных натуральных чисел
    var n, i, S : word;
    	begin
    	write ( ’ n= ’ );
    	readln ( n );
    	S : = 0;
    	i : = 2;
    	while i<=n do
    	begin
    		S:=S+i;
    		i := i +2;
    	end;
    	writeln ( ’ S= ’, S );
    end.
    

    Эту же задачу можно решить иначе, используя цикл for..do:

    var n, i, S : word;
    	begin
    		write ( ’ n= ’ );
    		readln ( n );
    		S : = 0;
    	for i :=1 to n do
    	{Если остаток от деления параметра цикла на 2 равен 0,}
    			{то это число четное, следовательно,}
    			{происходит накапливание суммы.}
    		if i mod 2 = 0 then
    			S:=S+i;
    	writeln ( ’ S= ’, S );
    end.
    

    В таблице 3.6 приведены результаты тестирования программы для n=7. Несложно заметить, что при нечётных значениях параметра цикла значение переменной, предназначенной для накапливания суммы, не изменяется.

    Суммирование чётных чисел
    i 1 2 3 4 5 6 7
    S 0 0 2 2 6 6 12 12
    ЗАДАЧА 3.13. Дано натуральное число $$N$$. Определить $$K$$ — количество делителей этого числа, меньших его (например, для $$N = 12$$ делители 1, 2, 3, 4, 6. Количество $$K = 5$$).

    Входные данные: N — целое число.

    Выходные данные: целое число K — количество делителей N.

    Промежуточные переменные: i — параметр цикла, возможные делители числа N.

    В блок-схеме, изображённой на рис. 3.32, реализован следующий алгоритм: в переменную K, предназначенную для подсчёта количества делителей заданного числа, помещается значение, которое не влияло бы на результат (k=0). Далее организовывается цикл, в котором изменяющийся параметр i выполняет роль возможных делителей числа N. Параметр цикла меняется от 1 до N/2 c шагом 1. Если заданное число делится нацело на параметр цикла, это означает, что i является делителем N, и значение переменной K следует увеличить на единицу. Цикл необходимо повторить N/2 раз.

    В таблице 3.7 отображены результаты тестирования алгоритма при определении делителей числа N=12.

    Определение количества делителей числа N
    i 1 2 3 4 5 6
    K 0 1 2 3 4 4 5
    (рис 3.32) Алгоритм подсчёта делителей натурального числа

    Текст программы, соответствующий описанному алгоритму:

    var N, i,K: word;
    	begin
    	write ( ’N= ’ );
    	readln (N);
    	K: = 0;
    	for i :=1 to N div 2 do
    		{Если N делится нацело на i, то}
    		if N mod i= 0 then
    			k:=K+1; {увеличить счетчик на единицу.}
    	writeln ( ’ K= ’,K);
    end.
    

    ЗАДАЧА 3.14. Дано натуральное число $$N$$. Определить, является ли оно простым. Натуральное число $$N$$ называется простым, если оно делится нацело без остатка только на единицу и $$N$$. Число 13 — простое, так как делится только на 1 и 13, N = 12 не является простым, так как делится на 1, 2, 3, 4, 6 и 12.

    Входные данные: N — целое число.

    Выходные данные: сообщение.

    Промежуточные данные: i — параметр цикла, возможные делители числа N.

    Алгоритм решения этой задачи (рис. 3.33) заключается в том, что необходимо определить, есть ли у числа N делители среди чисел от 2 до N/2. Если делителей нет — число простое. Предположим, что число N является простым (Pr:=true). Организуем цикл, в котором переменная i будет изменяться от 2 до N/2. В цикле будем проверять, делится ли N на i. Если делится, то мы нашли делитель, N не является простым (Pr:=false). Проверка остальных делителей не имеет смысла, аварийно покидаем цикл.

    (рис 3.33) Алгоритм определения простого числа

    В алгоритме предусмотрено два выхода из цикла. Первый — естественный, при исчерпании всех значений параметра, а второй — досрочный. После выхода из цикла надо проверить значение Pr. Если Pr=true, то число N — простое, иначе N не является простым числом.

    При составлении программы на языке Free Pascal досрочный выход из цикла удобно выполнять при помощи оператора break:

    var
    	N, i : integer;
    	Pr : boolean;
    	begin
    	write ( ’N= ’ );
    	readln (N);
    	Pr:= true; {Предположим, что число простое.}
    	for i :=2 to N div 2 do
    		{Если найдется хотя бы один делитель, то}
    		if N mod i = 0 then
    		begin
    			Pr:= false; {число простым не является и}
    			break; {досрочный выход из цикла.}
    		end;
    		{Проверка значения логического параметра, и}
    		if Pr then
    			{вывод на печать соответствующего сообщения.}
    			writeln ( ’Число  ’,N, ’ - простое ’ )
    		else
    			writeln ( ’Число  ’,N, ’  простым не является ’ );
    end.
    
    ЗАДАЧА 3.15. Определить количество простых чисел в интервале от $$N$$ до $$M$$, где $$N$$ и $$M$$ — натуральные числа, причем $$N \le M$$.

    Алгоритм решения данной задачи представлен на рис. 3.34.

    Обратите внимание, что здесь осуществляется проверка корректности ввода исходных данных. Если границы интервала не положительны, или значение N превышает M, ввод данных повторяется в цикле с постусловием до тех пор, пока не будут введены корректные исходные данные. Далее для каждого числа из указанного интервала (параметр i принимает значения от N до M) происходит проверка. Если число является простым, то переменная k увеличивается на единицу. Подробно определение простого числа описано в задаче 3.14.

    Программа на языке Free Pascal, реализующая алгоритм подсчёта количества простых чисел в заданном диапазоне:

    var N,M, i, j, k : longint;
    Pr : boolean;
    begin
    	repeat
    		write ( ’N= ’ );
    		readln (N);
    		write ( ’M= ’ );
    		readln (M);
    	until (N>0) and (M>0) and (N<M);
    	k : = 0; {Количество простых чисел.}
    	for i :=N to M do {Параметр i принимает значения от N до M.}
    	begin
    		{Определение простого числа.}
    		Pr:= true;
    		for j :=2 to i div 2 do
    			if i mod j = 0 then
    			begin
    				Pr:= false;
    				break;
    			end;
    			{Если число простое, увеличиваем количество на 1.}
    			if Pr then
    				k:=k+1;
    	end;
    	if k=0 then writeln ( ’Простых чисел в диапазоне нет ’ )
    	else writeln ( ’Простых чисел в диапазоне  ’, k );
    end.
    
    (рис 3.34) Алгоритм определения простых чисел в заданном интервале ЗАДАЧА 3.16. Дано натуральное число $$N$$. Определить количество цифр в числе.

    Входные данные: N — целое число.

    Выходные данные: kol — количество цифр в числе.

    Промежуточные данные: M — переменная для временного хранения значения N.

    Для того чтобы подсчитать количество цифр в числе, необходимо определить, сколько раз заданное число можно разделить на десять нацело. Например, пусть N=12345, тогда количество цифр kol = 5. Результаты вычислений сведены в таблицу 3.8. Алгоритм определения количества цифр в числе представлен на рис. 3.35.

    Определение количества цифр числа
    kol N
    1 12345
    2 12345 div 10=1234
    3 1234 div 10=123
    4 123 div 10=12
    5 12 div 10=1
    1 div 10=0

    Текст программы, реализующей данную задачу, можно записать так:

    var
    	M,N: longint;
    	kol : word;
    begin
    	{Так как речь идёт о натуральных числах,}
    	{при вводе предусмотрена проверка.}
    	{Закончить цикл, если введено положительное число,}
    	{иначе повторить ввод}
    	repeat
    		write ( ’N= ’ );
    		readln (N);
    	until N>0;
    	M:=N; {Сохранить значение переменной N. }
    	kol : = 1; {Пусть число состоит из одной цифры.}
    	while M div 10 > 0 do
    	{Выполнять тело цикла, пока число делится нацело на 10.}
    	begin
    		kol := kol +1; {Счётчик количества цифр.}
    		M:=M div 1 0; {Изменение числа.}
    	end;
    	writeln ( ’ kol= ’, kol );
    end.
    
    (рис 3.35) Алгоритм определения количества цифр в числе ЗАДАЧА 3.17. Дано натуральное число $$N$$. Определить, содержит ли это число нули и в каких разрядах они расположены (например, число 1 101 111 011 содержит ноль в третьем и восьмом разрядах).

    Входные данные: N — целое число.

    Выходные данные: pos — позиция цифры в числе.

    Промежуточные данные: i — параметр цикла, M — переменная для временного хранения значения N.

    В связи с тем, что разряды в числе выделяются, начиная с последнего, то для определения номера разряда в числе, необходимо знать количество цифр в числе12 Алгоритм нахождения количества цифр в числе был рассмотрен в предыдущей задаче. . Таким образом, на первом этапе решения задачи необходимо определить kol — количество цифр в числе. Затем начинаем выделять из числа цифры; если очередная цифра равна нулю, нужно вывести на экран номер разряда, который занимает эта цифра. Процесс определения текущей цифры числа N=120405 представлен в таблице 3.9.

    Блок-схема алгоритма решения данной задачи показана на рис. 3.36.

    (рис 3.36) Алгоритм решения задачи 3.17
    Определение текущей цифры числа
    i Число М Цифра Номер позиции
    1 120405 div 10=12040 120405 mod 10=5 6
    2 12040 div 10=1204 12040 mod 10=0 5
    3 1204 div 10=120 1204 mod 10=4 4
    4 120 div 10=12 120 mod 10=0 3
    5 12 div 10=1 12 mod 10=2 2
    6 1 div 10=0 1 mod 10=1 1

    Текст программы, реализующей данный алгоритм:

    var
    	M,N: longint;
    	i, pos, kol : word;
    begin
    	{Так как речь идет о натуральных числах,}
    	{при вводе предусмотрена проверка.}
    	{Закончить цикл, если введено положительное число,}
    	{иначе повторить ввод}
    	repeat
    		write ( ’N= ’ );
    		readln (N);
    	until N>0;
    	//Определение kol - количества разрядов.
    	M:=N; {Сохранить значение переменной N.}
    	kol : = 1; {Пусть число состоит из одной цифры.}
    	while M div 10 > 0 do
    	{Выполнять тело цикла, пока число делится нацело на 10.}
    	begin
    		kol := kol +1; {Счётчик количества цифр.}
    		M:=M div 1 0; {Изменение числа.}
    	end;
    	writeln ( ’ kol= ’, kol );
    	M:=N;
    	pos : = 0; {Пусть в числе нет нулей.}
    	for i :=1 to kol do
    	begin
    		{Выделение цифры из числа и сравнение её с нулем.}
    		if (M mod 10 = 0) then
    		begin
    		pos := kol-i +1; {Позиция нуля в числе.}
    		writeln ( ’Ноль в  ’, pos, ’м- разряде. ’ );
    		end;
    		M:=M div 1 0; {Изменение числа.}
    	end;
    	if pos=0 then writeln ( ’Число не содержит цифру 0. ’ );
    end.
    

    3.5.6 Ввод данных из диалогового окна в среде Lazarus

    Окно ввода — это стандартное диалоговое окно, которое появляется на экране в результате вызова функции InputBox. В общем виде оператор ввода данных с использованием этой функции записывают так:

    имя:= InputBox (заголовок_окна,подсказка,значение );

    где

  • заголовок_окна — строка, определяющая название окна;
  • подсказка — текст поясняющего сообщения;
  • значение — строка, которая будет находиться в поле ввода при появлении окна на экране;
  • имя — переменная строкового типа, которой будет присвоено значение из поля ввода.
  • После выполнения фрагмента программы

    var S : string;
    begin
    	S:= InputBox ( ’ЗАГОЛОВОК ОКНА ’,
    		’Подсказка :  введите исходные данные ’, ’Данное значение ’ );
    end;
    

    появится окно, представленное на рис. 3.37.

    (рис 3.37) Окно ввода

    У пользователя есть возможность изменять текст в поле ввода. Щелчок по кнопке ОК приведёт к тому, что в переменную, указанную слева от оператора присваивания, будет занесена строка, находящаяся в поле ввода. В данном случае в переменную S будет записана строка ’Данное значение’. Щелчок по кнопке Cancel закроет окно ввода.

    Учитывая, что функция InputBox возвращает строковое значение, при вводе числовых данных применяют функции преобразования типов:

    var S : string; gradus, radian : real;
    begin
    	S:= InputBox ( ’Ввод данных ’,
    			’Введите величину угла в радианах ’, ’ 0,000 ’ );
    	gradus := StrToFloat ( S );
    	radian := gradus * pi / 1 8 0;
    	MessageDlg ( ’Величина угла в градусах ’
    			+FloatToStr ( radian ), MtInformation, [ mbOk ], 0 );
    end;
    

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

    ЗАДАЧА 3.18. Поступает последовательность из $$N$$ вещественных чисел. Определить наибольший элемент последовательности.

    Входные данные: N — целое число; Х — вещественное число, определяет текущий элемент последовательности.

    Выходные данные: Max — вещественное число, элемент последовательности с наибольшим значением.

    Промежуточные переменные: i — параметр цикла, номер вводимого элемента последовательности.

    Алгоритм поиска наибольшего элемента в последовательности следующий (рис. 3.38).

    В памяти компьютера отводится ячейка, например, с именем Max, в которой будет храниться наибольший элемент последовательности — максимум. Вводим количество элементов последовательности и первый элемент последовательности. Предполагаем, что первый элемент последовательности наибольший, и записываем его в Max. Затем вводится второй элемент последовательности и сравнивается с предполагаемым максимумом. Если окажется, что второй элемент больше, его записываем в ячейку Max. В противном случае никаких действий не предпринимаем. Потом переходим к вводу следующего элемента последовательности, и алгоритм повторяется с начала. В результате, в ячейке Max будет храниться элемент последовательности с наибольшим значением Для поиска наименьшего элемента последовательности (минимума), предполагают, что первый элемент — наименьший, записывают его в ячейку Min, а затем среди элементов последовательности ищут число, значение которого будет меньше, чем предполагаемый минимум .

    Разместим на форме объект типа надпись Label1 и кнопку Button1 (см. рисунок 3.39). Щелчок по кнопке приведёт к выполнению следующей процедуры:

    (рис 3.38) Алгоритм поиска наибольшего числа в последовательности
    procedure TForm1. Button1Click ( Sender : TObject );
    var i,N: integer; max,X: real; S : string; begin
    	//Ввод количества элементов последовательности.
    	S:= InputBox ( ’Ввод ’,
    	’Введите_количество элементов в последовательности. ’, ’ 0 ’ );
    	N:= StrToFloat ( S );
    	//Ввод первого элемента последовательности.
    	S:= InputBox ( ’Ввод элементов последовательности ’,
    			’Введите число. ’, ’ 0 ’ );
    	X:= StrToFloat ( S );
    	//Предположим, что первый элемент максимальный — Max=X.
    	max:=X;
    	//Параметр цикла принимает стартовое значение i=2,
    	//т.к. первый элемент уже введён.
    	for i :=2 to N do
    	begin
    		//Ввод следующих элементов последовательности.
    		S:= InputBox ( ’Ввод элементов последовательности ’,
    		’Введите число. ’, ’ 0 ’ );
    		X:= StrToInt ( S );
    		//Если найдётся элемент, превышающий максимум,
    		//записать его в ячейку Max —
    		//теперь он предполагаемый максимум.
    		if X>max then max:=X;
    	end;
    	//Вывод наибольшего элемента последовательности.
    	MessageDlg ( ’Значение наибольшего элемента -  ’
    		+FloatToStr (max ), MtInformation, [ mbOk ], 0 );
    end;
    

    Результаты работы программы представлены на рис. 3.39,3.40,3.41,3.42.

    (рис 3.39) Первое окно диалога к задаче 3.18 (рис 3.40) Второе окно диалога к задаче 3.18 (рис 3.41) Третье окно диалога к задаче 3.18 (рис 3.42) Результат работы программы к задаче 3.18 ЗАДАЧА 3.19. Вводится последовательность целых чисел, 0 — конец последовательности. Найти наименьшее число среди положительных, если таких значений несколько, определить, сколько их.

    Блок-схема решения задачи приведена на рис. 3.43.

    (рис 3.43) Алгоритм поиска минимального положительного числа в последовательности

    Далее приведён текст подпрограммы с подробными комментариями Алгоритм поиска максимального (минимального) элементов последовательности подробно описан в задаче 3.18. . Подпрограмма выполняется при обращении к кнопке Button1, предварительно размещённой на форме.

    procedure TForm1. Button1Click ( Sender : TObject );
    	var N, k, min : integer; S : string;
    	begin
    	//Ввод первого элемента последовательности.
    	S:= InputBox ( ’Ввод элементов последовательности ’,
    		’введите_число.  0 - конец последовательности ’, ’ 0 ’ );
    	N:= StrToInt ( S );
    //Предположим, что положительных чисел нет — K=0. В переменной
    //K будет храниться количество минимумов среди положительных
    //чисел.
    	k : = 0;
    	//Пока введённое число не равно нулю, выполнять тело цикла.
    	while N<>0 do
    	begin
    		//Проверяем, является ли введённое число положительным.
    		if N>0 then
    		begin
    //Если N>0 и K=0, поступил 1-й положительный элемент,
    //предположим, что он минимальный Min=N, соответственно,
    //количество минимумов равно 1.
    		if k=0 then
    		begin
    			k : = 1;
    			min:=N;
    		end
    		//Если элемент не первый, сравниваем его
    		//с предполагаемым минимумом;
    		//если элемент меньше, записываем его
    		//в Min и обнуляем счетчик.
    		else if N<min then
    			begin
    				min:=N;
    				k : = 1;
    			end
    //Если элемент равен минимуму,
    		//увеличиваем количество минимумов на 1.
    		else if N=min then k:=k+1;
    	end;
    		//Ввод следующего элемента последовательности.
    		S:= InputBox ( ’Ввод элементов последовательности ’,
    			’введите число.  0 - конец последовательности ’, ’ 0 ’ );
    		N:= StrToInt ( S );
    	end; //Конец цикла.
    	if k<>0 then
    		//Если значение счётчика не равно нулю,
    		//выводим значение минимального элемента и
    		//количество таких элементов,
    		MessageDlg ( ’MIN =  ’+IntToStr ( min)+
    			’  K= ’+IntToStr ( k ), MtInformation, [ mbOk], 0 )
    	else
    		//в противном случае сообщаем,
    		//что положительных чисел нет.
    		MessageDlg ( ’Положительных чисел нет ’,
    			MtInformation, [ mbOk ], 0 );
    end;
    
    ЗАДАЧА 3.20. Определить, сколько раз последовательность из $$N$$ произвольных чисел меняет знак.

    Чтобы решить задачу, нужно попарно перемножать элементы последовательности. Если результат произведения пары чисел — отрицательное число, значит, эти числа имеют разные знаки.

    Пусть $$k$$ — количество смен знака последовательности — равно 0.

    Пусть в переменной А хранится текущий элемент последовательности, а в переменой В — предыдущий. Введём N — количество элементов последовательности. Организуем цикл (переменная i меняется от 1 до N). В цикле будем делать следующее: вводим очередной элемент последовательности (A); если это это первый элемент последовательности (i=1), то сравнивать его не с чем, и просто переписываем переменную A в переменную B (B:=A). Если это не первый последовательности ($$i \not = 1$$), то проверяем знак произведения $$A \cdot B$$ (текущего и предыдущего элемента последовательности). Если произведение отрицательно, то счётчик k увеличиваем на 1. После чего не забываем в переменную B записать A. Блок-схема алгоритма приведена на рис. 3.44.

    Разметим на форме два объекта типа надпись Label1 и Label2, объект поле ввода Edit1 и кнопку Button1 (рис. 3.45).

    Текст подпрограммы, которая будет выполнена при обращении к кнопке, приведён далее.

    (рис 3.44) Алгоритм к задаче 3.20 (рис 3.45) Форма программы решения задачи 3.20
    procedure TForm1. Button1Click ( Sender : TObject );
    var N, A, B, i, k : integer; S : string;
    begin
    	N:= StrToInt ( Edit1. Text );
    	S:= InputBox ( ’Ввод элементов последовательности ’,
    		’Введите число.  0 - конец_последовательности ’, ’ 0 ’ );
    	A:= StrToInt ( S );
    	k : = 0;
    	for i :=1 to N do
    	begin
    		S:= InputBox ( ’Ввод элементов последовательности ’,
    			’введите число. 0 - конец последовательности ’, ’ 0 ’ );
    		A:= StrToInt ( S );
    		if ( i <>1) then
    		if A*B<0 then k:=k+1;
    		B:=A;
    	end;
    	MessageDlg ( ’K =  ’+IntToStr ( k ), MtInformation, [ mbOk ], 0 ); end;
    

    Результаты работы программы представлены на рис. 3.46, 3.47.

    (рис 3.46) Второе окно диалога к задаче 3.20 (рис 3.47) Результат работы программы к задаче 3.20
    Страницы:

    3.1 Основные конструкции алгоритма

    Как правило, созданию программы предшествует разработка алгоритма Алгоритм — от algorithmi, algorismus, первоначально латинская транслитерация имени математика аль-Хорезми. . Алгоритм — это чёткое описание последовательности действий, которые необходимо выполнить для того, чтобы при соответствующих исходных данных получить требуемый результат. Одним из способов представления алгоритма является блок-схема. При составлении блок-схемы все этапы решения задачи изображаются с помощью различных геометрических фигур. Эти фигуры называют блоками и, как правило, сопровождают текстом. Последовательность выполнения этапов указывают при помощи стрелок, соединяющих эти блоки. Типичные этапы решения задачи изображаются следующими геометрическими фигурами:

  • блок начала (конца ) (рис. 3.1). Текст внутри блока: "начало" ("конец");
  • (рис 3.1) Блок начала (конца) алгоритма
  • блок ввода (вывода ) данных (рис. 3.2). Текст внутри блока: "ввод" ("вывод" или "печать") и список вводимых (выводимых) переменных;
  • (рис 3.2) Блок ввода (вывода)
  • блок решения, или арифметический (рис. 3.3). Внутри блока записывается действие, вычислительная операция или группа операций;
  • (рис 3.3) Арифметический блок
  • условный блок (рис. 3.4). Логическое условие записывается внутри блока. В результате проверки условия осуществляется выбор одного из возможных путей (ветвей) вычислительного процесса.
  • (рис 3.4) Условный блок

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

    Линейный процесс — это конструкция, представляющая собой последовательное выполнение двух или более блоков (рис. 3.5).

    Разветвляющийся процесс задаёт выполнение одного или другого оператора в зависимости от выполнения условия (рис. 3.6).

    Циклический процесс задаёт многократное выполнение оператора или группы операторов (рис. 3.7).

    (рис 3.5) Линейный процесс (рис 3.6) Разветвляющийся процесс (рис 3.7) Циклический процесс

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

    Рассмотрим операторы языка программирования Free Pascal, реализующие основные конструкции алгоритма.

    3.2 Оператор присваивания

    Оператор присваивания в языке Free Pascal состоит из двух символов: двоеточия и знака равенства. Символы := всегда пишут слитно. Пробелы допускаются перед символом двоеточия и после символа равенства.

    В общем случае оператор присваивания имеет вид:

    имя_переменной := значение;

    где значение — это выражение, переменная, константа или функция. Выполняется оператор так. Сначала вычисляется значение выражения, указанного в правой части оператора, а затем его результат записывается в область памяти (переменную), имя которой указано слева. Например, запись a:=b означает, что переменной а присваивается значение выражения b.

    Типы переменной a и выражения b должны совпадать или быть совместимыми для присваивания, то есть тип, к которому принадлежит b, должен находиться в границах типа переменной а.

    Оператор присваивания, как и любой другой оператор в языке Free Pascal, отделяется от других точкой с запятой.

    3.3 Составной оператор

    Составной оператор — группа операторов, отделённых друг от друга точкой с запятой, начинающихся со служебного слова begin и заканчивающихся служебным словом end:

    begin
    оператор_1;
    ...
    оператор_n
    end;
    

    Транслятор воспринимает составной оператор как один оператор.

    3.4 Условные операторы

    В языке Free Pascal одна из основных конструкций алгоритма, разветвляющийся процесс, реализована двумя условными операторами: if и case. Рассмотрим каждый из них.

    3.4.1 Условный оператор if..then..else

    При решении большинства задач порядок вычислений зависит от определённых условий, например от исходных данных или от промежуточных результатов, полученных на предыдущих шагах программы. Для организации вычислений в зависимости от какого-либо условия в языке Free Pascal используется условный оператор if..then..else, который в общем виде записывается так:

    (рис 3.8) Алгоритм условного оператора if..then..else

    if условие then оператор_1 else оператор_2;

    где if..then..else — зарезервированные слова, условие — выражение логического типа Логическое выражение может принимать одно из двух значений: истина или ложь. , оператор_1 и оператор_2 — любые операторы языка Free Pascal.

    Работа условного оператора организована следующим образом. Сначала вычисляется выражение, записанное в условии. Если оно имеет значение истина (True), то выполняется оператор_1. В противном случае, когда выражение имеет значение ложь (False), оператор_1 игнорируется и управление передаётся оператору_2.

    Алгоритм, который реализован в условном операторе if..then..else, представлен на рис. 3.8.

    Например, чтобы сравнить значения переменных $$x$$ и $$y$$, нужно создать следующий программный код:

    write ( ’ x= ’ ); readln ( x );
    write ( ’ y= ’ ); readln ( y );
    if x=y then
    writeln ( ’значение x равно значению y ’ )
    else writeln ( ’значение x не равно значению y ’ );
    

    Если в задаче требуется, чтобы в зависимости от значения условия выполнялся не один оператор, а несколько, необходимо использовать составной оператор:

    if условие then
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end
    else
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end;
    

    Альтернативная ветвь else в условном операторе может отсутствовать, если в ней нет необходимости:

    if условие then оператор;
    или
    if условие then
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end;
    

    В таком "усечённом" виде условный оператор работает так: оператор (группа операторов) либо выполняется, либо пропускается в зависимости от значения выражения, представляющего условие. Алгоритм этого условного процесса представлен на рис. 3.9.

    Пример применения условного оператора, без альтернативной ветви else может быть таким:

    write ( ’ x= ’ ); readln ( x );
    write ( ’ y= ’ ); readln ( y );
    c : = 0;
    {Значение переменной c изменяется только при условии,}
    {что x не равно y.}
    if ( x<>y ) then c :=x+y;
    {Вывод на экран значения переменной c}
    {выполняется в любом случае.}
    writeln ( ’Значение переменной с= ’, c );
    

    Условные операторы могут быть вложены друг в друга. При вложениях условных операторов всегда действует правило: альтернатива else считается принадлежащей ближайшему if, имеющему ветвь else. Например, в записи он принадлежит оператору if с условием_1.

    (рис 3.9) Алгоритм условного оператора if без альтернативной ветви else
    if условие_1 then
    	if условие_2 then
    		оператор_А
    	else оператор_Б;
    

    оператор_Б относится к условию_2, а в конструкции

    if условие_1 then
    	begin
    		if условие_2 then
    			оператор_А;
    		end
    	else оператор_Б;
    

    Для сравнения переменных в условных выражениях применяют операции отношения : =, <>, <, >, <=, >=. Условные выражения составляют с использованием логических операций and, or и not. В языке Free Pascal приоритет операций отношения меньше, чем у логических операций, поэтому составные части сложного логического выражения заключают в скобки.

    Допустим, нужно проверить, принадлежит ли переменная $$x$$ интервалу $$[a, b]$$. Условный оператор будет иметь вид:

    if ( x>=a ) and ( x<=b ) then...

    Запись

    if x>=a and x<=b then...

    неверна, так как фактически будет вычисляться значение выражения

    x>=(a and x)<=b.

    (рис 3.10) Графическое представление задачи 3.1

    Рассмотрим использование оператора if на примерах В задачах этой главы мы не будем уделять много внимания интерфейсу создаваемых программ, чтобы у читателя была возможность разобраться в алгоритмах и способах их записи на языке Free Pascal. .

    ЗАДАЧА 3.1. Дано вещественное число $$x$$. Для функции, график которой приведён на рис. 3.10, вычислить $$y = f (x)$$.

    Аналитически функцию, представленную на рис. 3.10, можно записать так:

    $$y(x) = \begin{cases} 4, x \le -2, \\ x^2, -2 < x < 1, \\ 1, x \ge 1. \end{cases}$$

    Составим словесный алгоритм решения этой задачи:

  • Начало алгоритма.
  • Ввод числа $$x$$ (аргумент функции).
  • Если значение $$x$$ меньше либо равно -2, то переход к п. 4, иначе переход к п. 5.
  • Вычисление значения функции: $$y = 4$$, переход к п. 8.
  • Если значение $$x$$ больше либо равно 1, то переход к п. 6, иначе переход к п. 7.
  • Вычисление значения функции: $$y = 1$$, переход к п. 8.
  • Вычисление значения функции: $$y = x^2$$.
  • Вывод значений аргумента $$x$$ и функции $$y$$.
  • Конец алгоритма.
  • Блок-схема, соответствующая описанному алгоритму, представлена на рисунке 3.11. Как видно, блок-схема нагляднее и проще для восприятия, чем словесное описание алгоритма. В дальнейшем для описания алгоритма мы часто будем использовать именно блок-схемы.

    (рис 3.11) Блок-схема алгоритма решения задачи 3.1

    Текст программы на языке Free Pascal будет иметь вид:

    var
    	x, y : real;
    begin
    	write ( ’ x= ’ );
    	readln ( x );
    	if x<= -2 then y:=4
    	else if x>=1 then y:=1
    	else y:= sqr ( x );
    	writeln ( ’ x= ’, x : 5 : 2, ’    y= ’, y : 5 : 2 );
    end.
    

    Эту программу можно ввести в текстовом редакторе Geany (или в текстовом редакторе Free Pascal) и запустить на выполнение.

    ЗАДАЧА 3.2. Даны вещественные числа $$x$$ и $$y$$. Определить, принадлежит ли точка с координатами $$(x; y)$$ заштрихованной части плоскости (рис. 3.12).

    Как показано на рис. 3.12, область ограничена линиями $$x = -1, x = 3, y = -2$$ и $$y = 4$$. Значит, точка с координатами $$(x; y)$$ будет принадлежать этой области, если будут выполняться следующие условия: $$x \ge -1, x \le 3, y \ge -2$$ и $$y \le 4$$. Иначе точка лежит за пределами области.

    Блок-схема, описывающая алгоритм решения данной задачи, представлена на рис. 3.13.

    (рис 3.12) Графическое представление задачи 3.2 (рис 3.13) Алгоритм решения задачи 3.2

    Текст программы к задаче 3.2:

    var x, y : real;
    begin
    	write ( ’ x= ’ ); readln ( x );
    	write ( ’ y= ’ ); readln ( y );
    	if ( x>= -1) and ( x<=3) and ( y>= -2) and ( y<=4) then
    		writeln ( ’Точка принадлежит области ’ )
    	else
    		writeln ( ’Точка не принадлежит области ’ );
    end.
    
    ЗАДАЧА 3.3. Написать программу решения квадратного уравнения ax^2+ bx + c = 0.

    Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.

    (рис 3.14) Алгоритм решения квадратного уравнения

    Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — корни квадратного уравнения — либо сообщение о том, что корней нет.

    Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.

    Составим словесный алгоритм решения этой задачи.

  • Начало алгоритма.
  • Ввод числовых значений переменных $$a, b$$ и $$c$$.
  • Вычисление значения дискриминанта $$d$$ по формуле $$d = b^2-4ac$$.
  • Если $$d < 0$$, то переход к п. 5, иначе переход к п. 6.
  • Вывод сообщения "Действительных корней нет" и переход к п. 8.
  • Вычисление корней $$x_1 = \left(-b+\sqrt{d}\right)/(2a)$$ и $$x_2 =\left(-b-\sqrt{d}\right)/(2a)$$.
  • Вывод значений $$x_1$$ и $$x_2$$ на экран.
  • Конец алгоритма.
  • Блок-схема, соответствующая этому описанию, представлена на рис. 3.14.

    Текст программы, которая реализует решение квадратного уравнения:

    {Описание переменных.}
    var a, b, c, d, x1, x2 : real;
    begin
    {Ввод значения коэффициентов квадратного уравнения.}
    writeln ( ’Введите коэффициенты квадратного уравнения ’ );
    readln ( a, b, c );
    {Вычисление дискриминанта.}
    d:=b * b-4*a*c;
    {Если дискриминант отрицателен,}
    if d<0 then
    	{то вывод сообщения, что корней нет,}
    	writeln ( ’Действительных корней нет ’ )
    else
    begin
    	{иначе вычисление корней x1, x2}
    	x1:=(-b+sqrt ( d ) ) / 2 / a;
    	x2:=(-b_sqrt ( d ) ) / ( 2 * a );
    	{и вывод их на экран.}
    	writeln ( ’X1= ’, x1 : 6 : 3, ’ -X2= ’, x2 : 6 : 3 )
    end
    end.
    
    ЗАДАЧА 3.4. Составить программу нахождения действительных и комплексных корней квадратного уравнения $$ax^2 + bx + c = 0$$.

    Исходные данные: вещественные числа $$a, b$$ и $$c$$ — коэффициенты квадратного уравнения.

    Результаты работы программы: вещественные числа $$x_1$$ и $$x_2$$ — действительные корни квадратного уравнения — либо $$x_1$$ и $$x_2$$ — действительная и мнимая части комплексного числа.

    Вспомогательные переменные: вещественная переменная $$d$$, в которой будет храниться дискриминант квадратного уравнения.

    Можно выделить следующие этапы решения задачи:

  • Ввод коэффициентов квадратного уравнения $$a, b$$ и $$c$$.
  • Вычисление дискриминанта $$d$$ по формуле $$d = b^2- 4ac$$.
  • Проверка знака дискриминанта. Если $$d \ge 0$$, то вычисление действительных корней: $$x_1 = \frac{-b+\sqrt{d}}{2a}\text{ и } x_2 = \frac{-b-\sqrt{d}}{2a}$$
  • и вывод их на экран. При отрицательном дискриминанте выводится сообщение о том, что действительных корней нет, и вычисляются комплексные корни Комплексные числа записываются в виде $$a+bi$$, где $$a$$ — действительная часть комплексного числа, $$b$$ — мнимая часть комплексного числа, $$i$$ — мнимая единица $$\sqrt{-1}$$.

    (рис 3.15) Алгоритм решения задачи 3.4 $$\frac{-b}{2a}+i\frac{\sqrt{|d|}}{2a}, \frac{-b}{2a}-i\frac{\sqrt{|d|}}{2a}.$$

    У обоих комплексных корней действительные части одинаковые, а мнимые отличаются знаком. Поэтому можно в переменной x1 хранить действительную часть числа $$-b/(2a)$$, в переменной x2 — модуль мнимой части $$\sqrt{|d|}/(2a)$$, а в качестве корней вывести x1+ix2 и x1-ix2.

    На рис. 3.15 изображена блок-схема решения задачи. Блок 1 предназначен для ввода коэффициентов квадратного уравнения. В блоке 2 осуществляется вычисление дискриминанта. Блок 3 осуществляет проверку знака дискриминанта; если дискриминант отрицателен, то корни комплексные, их расчёт происходит в блоке 4 (действительная часть корня записывается в переменную x1, модуль мнимой — в переменную x2), а вывод — в блоке 5 (первый корень x1+ix2, второй — x1-ix2). Если дискриминант положителен, то вычисляются действительные корни уравнения (блоки 6—7) и выводятся на экран (блок 8).

    Текст программы, реализующей поставленную задачу:

    var a, b, c, d, x1, x2 : real;
    begin
    	writeln ( ’Введите_коэффициенты_квадратного_уравнения ’ );
    	readln ( a, b, c );
    	d:=b * b-4*a*c;
    	if d<0 then
    	begin
    	//Если дискриминант отрицателен, то вывод сообщения,
    	//что действительных корней нет, и вычисление комплексных корней.
    		writeln ( ’Действительных корней нет ’ );
    		{Вычисление действительной части комплексных корней.}
    		x1:=-b /(2 * a );
    		{Вычисление модуля мнимой части комплексных корней.}
    		x2:= sqrt ( abs ( d ) ) / ( 2 * a );
    		writeln ( ’Комплексные корни уравнения  ’,
    		a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
    		{Вывод значений комплексных корней в виде $$x1\pm ix2$$}
    		writeln ( x1 : 1 : 2, ’+i * ( ’, x2 : 1 : 2, ’ ) ’ );
    		writeln ( x1 : 1 : 2, ’- i * ( ’, x2 : 1 : 2, ’ ) ’ );
    	end
    	else
    	begin
    	{иначе вычисление действительных корней x1, x2}
    		x1:=(-b+sqrt ( d ) ) / 2 / a;
    		x2:=(-b_sqrt ( d ) ) / ( 2 * a );
    		{и вывод их на экран.}
    		writeln ( ’Действительные корни уравнения  ’,
    			a : 1 : 2, ’ x^2+ ’, b : 1 : 2, ’ x+ ’, c : 1 : 2, ’=0 ’ );
    		writeln ( ’X1= ’, x1 : 1 : 2, ’ X2= ’, x2 : 1 : 2 )
    	end
    end.
    
    ЗАДАЧА 3.5. Составить программу для решения кубического уравнения $$ax^3 + bx^2 + cx + d = 0$$.

    Кубическое уравнение имеет вид

    $$ax^3 + bx^2 + cx + d = 0$$

    После деления на $$a$$ уравнение (3.1) принимает канонический вид:

    $$x^3 + rx^2 + sx + t = 0$$

    где $$r = b/a, s = c/a, t = d/a$$. В уравнении (3.2) сделаем замену $$x = y - r/3$$ и получим приведённое уравнение:

    $$y^3 + py + q = 0,$$

    где

    $$p = \frac{3s-r^2}{3}, q = \frac{2r^{3}}{27} - \frac{rs}{3}+t.$$

    Число действительных корней приведённого уравнения (3.3) зависит от знака дискриминанта $$D = (p/3)^3+ (q/2)^2$$ приведённого кубического уравнения (табл. 3.1).

    Количество корней кубического уравнения
    Дискриминант Количество действительных корней Количество комплексных корней
    D > 0 1 2
    D < 0 3

    Корни приведённого уравнения могут быть рассчитаны по формулам Кардано:

    $$\begin{aligned} y_{1} = u+v\\ y_{2} = \frac{-{u+v}}{2}+\frac{u-v}{2}i\sqrt{3}\\ y_{3} = \frac{-{u+v}}{2}-\frac{u-v}{2}i\sqrt{3} \end{aligned}$$

    где

    $$u=\sqrt[{3}]{-q/2+\sqrt{D}}, v=\sqrt[{3}]{-q/2-\sqrt{D}}.$$

    При отрицательном дискриминанте уравнение (3.1) имеет три действительных корня, но они будут вычисляться через вспомогательные комплексные величины. Чтобы избавиться от этого, можно воспользоваться формулами:

    $$\begin{aligned} y_1=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}\right),\\ y_2=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{2\pi}{3}\right),\\ y_3=2\sqrt[3]{\rho}\cos\left(\frac{\varphi}{3}+\frac{4\pi}{3}\right), \end{aligned}$$

    где

    $$\rho =\sqrt{\frac{-p^3}{27}}, \cos(\varphi)=\frac{-q}{2\rho}.$$

    Таким образом, при положительном дискриминанте кубического уравнения (3.3) расчёт корней будем вести по формулам (3.4), а при отрицательном — по формулам (3.5).

    После расчёта корней приведённого уравнения (3.3) по формулам (3.4) или (3.5) необходимо по формулам

    $$x_{k}=y_{k}-\frac{r}{3}, k=1,2,3\ldots$$

    перейти к корням заданного кубического уравнения (3.1).

    Блок-схема решения кубического уравнения представлена на рис. 3.16.

    (рис 3.16) Алгоритм решения кубического уравнения

    Описание блок-схемы. В блоке 1 вводятся коэффициенты кубического уравнения, в блоках 2—3 рассчитываются коэффициенты канонического и приведённого уравнений. Блок 4 предназначен для вычисления дискриминанта. В блоке 5 проверяется знак дискриминанта кубического уравнения. Если он отрицателен, то корни вычисляются по формулам (3.5) (блоки 6—7). При положительном значении дискриминанта расчёт идет по формулам (3.4) (блок 9, 10). Блоки 8 и 11 предназначены для вывода результатов на экран.

    Текст программы с комментариями приведён ниже5 При расчёте величин $$u$$ и $$v$$ в программе предусмотрена проверка значения подкоренного выражения. Если $$\frac{-q}{2}\pm \sqrt{D} > 0$$, то $$u=\sqrt[3]{\frac{-q}{2}+ \sqrt{D}} > 0$$, а $$v=\sqrt[3]{\frac{-q}{2}- \sqrt{D}} > 0$$. Если $$\frac{-q}{2}\pm \sqrt{D} < 0$$, то $$u=\sqrt[3]{\frac{-q}{2}+ \sqrt{D}} > 0$$, а $$v=\sqrt[3]{\frac{-q}{2}- \sqrt{D}} > 0$$. Соответственно, при нулевом значении подкоренного выражения $$u$$ и $$v$$ обращаются в ноль. .

    var
    	a, b, c, d, r, s, t, p, q, ro, f i, x1, x2, x3, u, v, h, g : real;
    begin
    	//Ввод коэффициентов кубического уравнения.
    	write ( ’ a= ’ ); readln ( a );
    	write ( ’ b= ’ ); readln ( b );
    	write ( ’ c= ’ ); readln ( c );
    	write ( ’ d= ’ ); readln ( d );
    	//Расчёт коэффициентов канонического уравнения по 3.2.
    	r :=b/a; s := c /a; t :=d/a;
    	//Вычисление коэффициентов приведённого уравнения 3.3.
    	p :=(3 * s - r * r ) / 3;
    	q:=2* r * r * r /27- r * s/3+ t;
    	//Вычисление дискриминанта кубического уравнения.
    	d :=( p /3) * sqr ( p/3)+ sqr ( q / 2 );
    	//Проверка знака дискриминанта,
    	//ветка then реализует формулы (3.5),
    	//ветка else — формулы 3.4
    	if d<0 then
    	begin
    		ro := sqrt (-p * p * p / 27 );
    		//Следующие два оператора реализуют расчёт угла fi,
    		//сначала вычисляется величина косинуса угла,
    		//затем вычисляется его арккосинус через арктангенс.
    		fi :=-q /(2 * ro );
    		fi := pi /2-arctan ( fi / sqrt (1 - fi * fi ) );
    		//Вычисление действительных корней уравнения x1, x2 и x3
    		x1 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3) - r / 3;
    		x2 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+2 * pi /3) - r / 3;
    		x3 :=2 * exp (1/3 * ln ( ro ) ) * cos ( fi /3+4 * pi /3) - r / 3;
    		writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, x2 : 1 : 3, ’ x3= ’, x3 : 1 : 3 );
    end
    else
    begin
    	//Вычисление u и v с проверкой знака
    	// подкоренного выражения.
    	if -q/2+ sqrt ( d)>0 then
    		u:=exp (1/3 * ln (-q/2+ sqrt ( d ) ) )
    	else
    		if -q/2+ sqrt ( d)<0 then
    			u:=-exp (1/3 * ln ( abs(-q/2+ sqrt ( d ) ) ) )
    		else
    			u : = 0;
    	if -q/2- sqrt ( d)>0 then
    		v:=exp (1/3 * ln (-q/2- sqrt ( d ) ) )
    	else
    		if -q/2- sqrt ( d)<0 then
    			v:=-exp (1/3 * ln ( abs(-q/2- sqrt ( d ) ) ) )
    		else
    			v : = 0;
    	//Вычисление действительного корня кубического уравнения.
    	x1:=u+v-r / 3;
    	//Вычисление действительной и
    	// мнимой части комплексных корней.
    	h:=-(u+v)/2 - r / 3;
    	g :=(u - v )/2 * sqrt ( 3 );
    	writeln ( ’ x1= ’, x1 : 1 : 3, ’ x2= ’, h : 1 : 3, ’+i*  ’, g : 1 : 3,
    				’ x3= ’, h : 1 : 3, ’ -i*  ’, g : 1 : 3 );
    	end
    end.
    
    ЗАДАЧА 3.6. Заданы коэффициенты a, b и c биквадратного уравнения $$ax^4 + bx^2+ c = 0$$. Найти все его действительные корни.

    Входные данные: a, b, c. Выходные данные: x1, x2, x3, x4.

    Для решения биквадратного уравнения необходимо заменой $$y = x^2$$ привести его к квадратному уравнению $$ay^2 + by + c = 0$$ и решить это уравнение.

    Опишем алгоритм решения этой задачи (рис. 3.17):

  • Ввод коэффициентов биквадратного уравнения $$a, b$$ и $$c$$ (блок 1).
  • Вычисление дискриминанта уравнения $$d$$ (блок 2).
  • Если $$d < 0$$ (блок 3), вывод сообщения, что корней нет (блок 4), а иначе определяются корни соответствующего квадратного уравнения $$y_1$$ и $$y_2$$ (блок 5).
  • Если $$y_1 < 0$$ и $$y_2 < 0$$ (блок 6), то вывод сообщения, что корней нет (блок 7).
  • Если $$y_1 \ge 0$$ и $$y_2 \ge 0$$ (блок 8), то вычисляются четыре корня по формулам $$\pm \sqrt{y_1}, \pm \sqrt{y_2}$$ (блок 9) и выводятся значения корней (блок 10).
  • Если условия 4) и 5) не выполняются, то необходимо проверить знак $$y_1$$. Если $$y_1 > 0$$ (блок 11), то вычисляются два корня по формуле $$\pm \sqrt{y_1}$$ (блок 12), иначе (если $$y_2 > 0$$) вычисляются два корня по формуле $$\pm \sqrt{y_2}$$ (блок 13).
  • Вывод вычисленных значений корней (блок 14).
  • (рис 3.17) Алгоритм решения биквадратного уравнения

    Текст программы на языке Free Pascal с комментариями:

    //Описание переменных:
    //a,b,c - коэффициенты биквадратного уравнения,
    //d - дискриминант,
    //x1,x2,x3,x4 - корни биквадратного уравнения,
    //y1,y2 - корни квадратного уравнения ay^2+by+c=0.
    var
    	a, b, c, d, x1, x2, x3, x4, y1, y2 : real;
    begin
    	//Ввод коэффициентов уравнения.
    	writeln ( ’Введите коэффициенты биквадратного уравнения ’ );
    	readln ( a, b, c );
    	//Вычисление дискриминанта.
    	d:=b * b-4*a*c;
    	//Если он отрицателен,
    	if d<0 then
    		//вывод сообщения "Корней нет"
    		writeln ( ’Корней нет ’ )
    	//Если дискриминант >= 0,
    	else
    	begin
    		//вычисление корней квадратного уравнения.
    		y1:=(-b+sqrt ( d ) ) / 2 / a;
    		y2:=(-b-sqrt ( d ) ) / ( 2 * a );
    		//Если оба корня квадратного уравнения < 0,
    		if ( y1 <0) and ( y2 <0) then
    			//вывод сообщения "Корней нет".
    			writeln ( ’Корней нет ’ )
    		//Если оба корня квадратного уравнения >= 0,
    		else if ( y1>=0) and ( y2>=0) then
    		begin
    			//вычисление четырех корней биквадратного уравнения.
    			x1:= sqrt ( y1 );
    			x2:=-x1;
    			x3:= sqrt ( y2 );
    			x4:=-sqrt ( y2 );
    			//Вывод корней биквадратного уравнения на экран.
    			writeln ( ’X1= ’, x1 : 6 : 3, ’ X2= ’, x2 : 6 : 3 );
    			writeln ( ’X3= ’, x3 : 6 : 3, ’ X4= ’, x4 : 6 : 3 );
    		end
    		//Если не выполнились оба условия
    		// 1. y1<0 И y2<0
    		// 2. y1>=0 И y2>=0,
    		//то проверяем условие y1>=0
    		else if ( y1>=0) then
    		//Если оно истинно
    		begin
    			x1:= sqrt ( y1 );
    			x2:=-x1;
    			writeln ( ’X1= ’, x1 : 6 : 3, ’   X2= ’, x2 : 6 : 3 );
    		end
    		else
    		//Если условие y1>=0 ложно, то
    		begin
    			x1:= sqrt ( y2 );
    			x2:=-x1;
    			writeln ( ’X1= ’, x1 : 6 : 3, ’    X2= ’, x2 : 6 : 3 );
    		end
    	end
    end.
    

    3.4.2 Оператор варианта case

    Оператор варианта case необходим в тех случаях, когда в зависимости от значений какой-либо переменной надо выполнить те или иные операторы.

    case выражение of
    	значение_1 : оператор_1;
    	значение_2 : оператор_2;
    	...
    	значение_N : оператор_N
    else
    	альтернативный_оператор
    end;
    

    Здесь выражение — переменная перечислимого типа (включая char и boolean), значение_1, значение_2,..., значение_N — это конкретное значение управляющей переменной или выражение, при котором необходимо выполнить соответствующий оператор, игнорируя остальные варианты. Значения в каждом наборе должны быть уникальны, то есть они могут появляться только в одном варианте. Пересечение наборов значений для разных вариантов является ошибкой.

    Оператор работает следующим образом. Вычисляется значение выражения. Затем выполняется оператор, помеченный значением, совпадающим со значением выражения. То есть, если выражение принимает значение_1, то выполняется оператор_1. Если выражение принимает значение_2, то выполняется оператор_2 и так далее. Если выражение не принимает ни одно из значений, то выполняется альтернативный_оператор, расположенный после ключевого слова else.

    Альтернативная ветвь else может отсутствовать, тогда оператор имеет вид:

    case выражение of
    	значение_1 : оператор_1;
    	значение_2 : оператор_2;
    	...
    	значение_N : оператор_N;
    end;
    

    Кроме того, в операторе case допустимо использование составного оператора. Например:

    case выражение of
    	значение_1 : begin оператор_A; оператор_B; end;
    	значение_2 : begin оператор_C; оператор_D; оператор_E; end;
    	...
    	значение_N : оператор_N;
    end;
    

    Рассмотрим применение оператора варианта на следующих примерах.

    ЗАДАЧА 3.7. Вывести на печать название дня недели, соответствующее заданному числу D, при условии, что в месяце 31 день и первое число — понедельник.

    Для решения задачи воспользуемся операцией mod, позволяющей вычислить остаток от деления двух чисел, и условием, что первое число — понедельник. Если в результате остаток от деления (обозначим его R) заданного числа D на семь будет равен единице, то это понедельник, двойке — вторник, тройке — среда и так далее. Следовательно, при построении алгоритма необходимо использовать семь условных операторов, как показано на рис. 3.18.

    Решение задачи станет значительно проще, если при написании программы воспользоваться оператором варианта:

    var d : byte;
    begin
    	write ( ’Введите число D= ’ );
    	readln (D);
    	case D mod 7 of {Вычисляется остаток от деления D на 7.}
    		{В зависимости от полученного значения}
    		{на печать выводится название дня недели}
    		1 : writeln ( ’ПОНЕДЕЛЬНИК ’ );
    		2 : writeln ( ’ВТОРНИК ’ );
    		3 : writeln ( ’СРЕДА ’ );
    		4 : writeln ( ’ЧЕТВЕРГ ’ );
    		5 : writeln ( ’ПЯТНИЦА ’ );
    		6 : writeln ( ’СУББОТА ’ );
    		0 : writeln ( ’ВОСКРЕСЕНЬЕ ’ );
    	end;
    end.
    
    (рис 3.18) Алгоритм решения задачи 3.7

    В предложенной записи оператора варианта отсутствует ветвь else. Это объясняется тем, что переменная R может принимать только одно из указанных значений, т. е. 1, 2, 3, 4, 5, 6 или 0.

    ЗАДАЧА 3.8. По заданному номеру месяца m вывести на печать название времени года.

    Для решения данной задачи необходимо проверить выполнение четырёх условий. Если заданное число m равно 12, 1 или 2, то это зима, если m попадает в диапазон от 3 до 5, то весна; лето определяется принадлежностью числа m диапазону от 6 до 8, и, соответственно, при равенстве переменной m 9, 10 или 11 это осень. Понятно, что область возможных значений переменной m находится в диапазоне от 1 до 12, и если пользователь введет число, не входящее в этот интервал, то появится сообщение об ошибке. Для этого в операторе case программы предусмотрена альтернативная ветка else.

    var m: byte;
    begin
    	write ( ’Введите номер месяца m= ’ ); readln (m);
    	//Проверка области допустимых значений переменной m.
    	case m of
    	//В зависимости от значения m на печать
    	//выводится название времени года.
    	1 2, 1, 2 : writeln ( ’ЗИМА ’ ); }
    	3.. 5 : writeln ( ’ВЕСНА ’ ); }
    	6.. 8 : writeln ( ’ЛЕТО ’ ); }
    	9.. 1 1 : writeln ( ’ОСЕНЬ ’ )}
    	//Если значение переменной m выходит за пределы области
    	//допустимых значений, то выдается сообщение об ошибке.
    	else writeln ( ’ОШИБКА ПРИ ВВОДЕ ! ! ! ’ );
    	end
    end.
    

    3.4.3 Обработка ошибок. Вывод сообщений в среде Lazarus

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

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

    MessageDlg (сообщение, тип_сообщения, [ список_кнопок ], справка );

    где

  • сообщение — текст, который будет отображён в окне сообщения;
  • тип_сообщения — определяет внешний вид окна (табл. 3.2);
  • список_кнопок — константы (перечисляются через запятую), определяющие тип кнопок окна сообщения (табл. 3.3);
  • справка — номер окна справочной системы, которое будет выведено на экран, если нажать F1; параметр равен нулю, если использование справки не предусмотрено.
  • Тип окна сообщения
    Параметр Тип окна сообщения
    mtInformation информационное
    mtWarning предупредительное
    mtError сообщение об ошибке
    mtConfirmation запрос на подтверждение
    mtCustom обычное
    Тип кнопки в окне сообщения
    Константа Кнопка
    mbYes Да
    mbNo Нет
    mbOk Ок
    mbCancel Отмена
    mbAbort Прервать
    mbRetry Повторить
    mbIgnore Пропустить
    mbHelp Помощь
    (рис 3.19) Конструирование формы для решения квадратного уравнения

    Вернёмся к задаче решения квадратного уравнения (задача 3.3). Нами был рассмотрен алгоритм решения этой задачи и написана программа на языке программирования Free Pascal. Реализуем эту задачу в среде Lazarus.

    Создадим новый проект Подробно о создании проекта см. в главе 1. (рис. 3.19).

    Для организации ввода коэффициентов уравнения внедрим на форму четыре объекта типа надпись (Label1, Label2, Label3, Label4) и три поля ввода (Edit1, Edit2, Edit3). Корни уравнения или сообщение об их отсутствии будем выводить в надпись Label5 На этапе конструирования формы сделаем метку Label5 невидимой, для чего свойство Visible установим в false (Label5.Visible:=false). . Все действия по вычислению корней квадратного уравнения будут выполняться при нажатии кнопки Button1.

    При вводе данных в программе могут возникнуть следующие ошибки:

  • в поле ввода оказалась строка, которую невозможно преобразовать в число;
  • значение коэффициента a равно нулю При вычислении корней произойдёт деление на ноль. .
  • Для того чтобы не допустить подобные ошибки, необходимо контролировать данные, вводимые пользователем. Применим для этой цели встроенную процедуру Val(S,X,Kod), которая преобразовывает строку S в число. Тип этого числа (целое или вещественное) зависит от типа переменной X. Если преобразование прошло успешно, то параметр Kod принимает значение, равное нулю, а результат преобразования записывается в переменную X. В противном случае Kod содержит номер позиции в строке S, где произошла ошибка, и содержимое переменной X не меняется. Далее приведён фрагмент программы с подробными комментариями:

    procedure TForm1. Button1Click ( Sender : TObject );
    var
    	a, b, c, d, x1, x2 : real;
    	kod1, kod2, kod3 : integer;
    begin
    	//Ввод значений коэффициентов квадратного уравнения.
    	//Из поля ввода Edit1 считывается строка символов и
    	//преобразовывается в вещественное число,
    	//если преобразование прошло успешно, то kod1=0 и
    	//полученное число записывается в переменную a.
    	val ( Edit1. Text, a, kod1 );
    	val ( Edit2. Text, b, kod2 );
    	val ( Edit3. Text, c, kod3 );
    	//Если преобразования прошли успешно, то
    	if ( kod1=0) and ( kod2=0) and ( kod3=0) then
    		//проверить чему равен первый коэффициент.
    		//Если значение первого коэффициента равно нулю, то
    			if a=0 then //выдать соответствующее сообщение.
    				MessageDlg ( ’Введите не нулевое значение а ’,
    					mtInformation, [ mbOk], 0 )
    		else //иначе перейти к решению квадратного уравнения
    		begin
    			d:=b * b-4*a*c;
    			Label5. Visible := true;
    			if d<0 then
    				Label5. Caption := ’В уравнении ’
    					+chr (13)+ ’нет действительных корней ’
    			else
    			begin
    				x1:=(-b+sqrt ( d ) ) / 2 / a;
    				x2:=(-b-sqrt ( d ) ) / ( 2 * a );
    				Label5. Caption := ’X1= ’+FloatToStr ( x1 )
    					+chr (13)+ ’X2= ’+FloatToStr ( x2 );
    			end;
    		end
    	else //Преобразование не выполнено, выдать сообщение.
    	MessageDlg ( ’Введите числовое значение ’,
    			mtInformation, [ mbOk ], 0 );
    end.
    
    (рис 3.20) Обработка ошибки ввода данных — коэффициент a равен 0

    Решение квадратного уравнения $$3x^2+4x+5=0$$

    (рис 3.21) Решение квадратного уравнения(корней нет) (рис 3.22) Обработка ошибки ввода данных — в поле ввода строка, которую невозможно преобразовать в число (рис 3.23) Вычисление корней квадратного уравнения

    Результаты работы программы показаны на рис. 3.20,рис. 3.21,рис. 3.22,3.23.

    Метод обработки ошибок ввода, изложенный в этом примере, не единственный. Можно попытаться контролировать символы, поступающие в поля ввода. Набор символов, которые могут быть преобразованы в число, невелик. Это все цифры от 0 до 9, знак минус и символ запятой. Кроме того, пользователь должен иметь возможность удалять символы из поля ввода. Для этого ему понадобится клавиша BackSpace (#8 — символ с кодом восемь). Представим набор перечисленных символов в виде множества, и если окажется, что введённый символ не принадлежит ему, то будем выводить сообщение об ошибке при вводе.

    Возможность контролировать ввод символов обеспечивает событие OnKeyPress. Если выделить компонент Edit2 и на вкладке инспектора объектов дважды щёлкнуть возле события нажатия клавиши OnKeyPress, то будет создана процедура обработки этого события. Текст процедуры с комментариями:

    //Обработка события ввод символа в поле Edit2.
    procedure TForm1. Edit2KeyPress ( Sender : TObject;
    		var Key : Char );
    begin //Если символ не принадлежит
    		//множеству допустимых символов,то
    	if not ( Key in [#8, ’, ’, ’_ ’, ’ 0 ’.. ’ 9 ’ ] ) then
    	begin //выдать сообщение и
    		MessageDlg ( ’Введите числовое значение ’,
    			mtInformation, [ mbOk ], 0 );
    		Abort; //прервать выполнение подпрограммы.
    end; end.
    

    3.5 Операторы цикла

    Циклический процесс, или просто цикл — это повторение одних и тех же действий. Последовательность действий, которые повторяются в цикле, называют телом цикла. Один проход цикла называют шагом, или итерацией Понятие итерации в математике и программировании несколько отличаются. В математике под итерацией понимают повторение какой-либо математической операции, использующее результат предыдущей аналогичной операции. В программировании итерация — это организация обработки данных, при которой действия повторяются многократно, не приводя при этом к вызовам самих себя (http://ru.wikipedia.org/wiki/Итерация ). . Переменные, которые изменяются внутри цикла и влияют на его окончание, называются параметрами цикла.

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

    В языке Free Pascal для удобства программиста предусмотрены три оператора, реализующих циклический процесс: while, repeat..until и for.

    3.5.1 Оператор цикла с предусловием while..do

    На рис. 3.24 изображена блок-схема алгоритма цикла с предусловием.

    Оператор, реализующий этот алгоритм, в языке Free Pascal имеет вид:

    while выражение do оператор;

    (рис 3.24) Алгоритм циклической структуры с предусловием

    Выражение должно быть логической константой, переменной или логическим выражением.

    Работает оператор while следующим образом. Вычисляется значение выражения.

    Если оно истинно (True), выполняется оператор. Выполнение цикла заканчивается, если условие ложно, после этого управление передается оператору, следующему за телом цикла. Выражение вычисляется перед каждой итерацией цикла. Если при первой проверке выражение ложно (False), цикл не выполнится ни разу.

    Если в цикле надо выполнить более одного оператора, необходимо использовать составной оператор:

    while условие do
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_n;
    	end;
    

    Рассмотрим пример. Пусть необходимо вывести на экран значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0;\pi]$$ с шагом 0.1.

    Применим цикл с предусловием :

    var x, y : real;
    begin
    	{Присваивание параметру цикла стартового значения.}
    	x : = 0;
    	{Цикл с предусловием.}
    	while x<=pi do {Пока параметр цикла не превышает}
    		{конечное значение, выполнять тело цикла.}
    	begin
    		{Вычислить значение y.}
    		y:=exp ( sin ( x ) ) * cos ( x );
    		{Вывод на экран пары х и y.}
    		writeln ( ’ x= ’, x, ’    y= ’, y );
    		{Изменение параметра цикла - переход к следующему значению x.}
    		x:=x + 0.1;
    	end; {Конец цикла.}
    end.
    

    В результате работы данного фрагмента программы на экран последовательно будут выводиться сообщения со значениями переменных x и y:

    x= 0; y=1
    x= 0.1; y =1.0995
    ...
    x= 3.1; y= -1.0415
    

    3.5.2 Оператор цикла с постусловием repeat..until

    Если в цикле с предусловием проверка условия осуществляется до тела цикла, то в цикле с постусловием условие проверяется после тела цикла (см. рис. 3.25). Сначала выполняются операторы, являющиеся телом цикла, после чего проверятся условие, если последнее ложно, то цикл повторяется. Выполнение цикла прекратится, если условие станет истинным.

    (рис 3.25) Алгоритм циклической структуры с постусловием

    В языке Free Pascal цикл с постусловием реализован конструкцией

    repeat
    	оператор;
    	until выражение;
    

    или, если тело цикла состоит более чем из одного оператора

    repeat
    	оператор_1;
    	оператор_2;
    	...
    	оператор_N;
    until выражение;
    

    Выражение должно быть логическим.

    Работает цикл следующим образом. В начале выполняется оператор, представляющий собой тело цикла. Затем вычисляется значение выражения. Если оно ложно (False), оператор тела цикла выполняется ещё раз. В противном случае цикл завершается, и управление передается оператору, следующему за циклом.

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

    Если применить цикл с постусловием для создания подпрограммы, которая выводит значения функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1, получим:

    var x, y : real;
    	begin
    	{Присваивание параметру цикла стартового значения.}
    	x : = 0;
    	{Цикл с постусловием.}
    	repeat {Начало цикла}
    		y:=exp ( sin ( x ) ) * cos ( x );
    		writeln ( ’ x= ’, x, ’   y= ’, y );
    		x:=x + 0.1; {Изменение значения параметра цикла.}
    	until x > pi; {Закончить работу цикла,}
    		{когда параметр превысит конечное значение.}
    	end.
    

    3.5.3 Оператор цикла for..do

    Операторы цикла с условием обладают значительной гибкостью, но не слишком удобны для организации "строгих" циклов, которые должны быть выполнены заданное число раз. Оператор цикла for..do используется именно в таких случаях:

    for параметр_цикла:=начальное_значение to конечное_значение do
    	оператор;
    for параметр_цикла:=конечное_значение downto начальное_значение do
    	оператор;
    

    где оператор — любой оператор языка, параметр_цикла — имя переменной целочисленного или перечислимого типов, начальное_значение и конечное_значение должны быть того же типа, что и параметр_цикла.

    Шаг изменения цикла for всегда постоянен и равен интервалу между двумя ближайшими значениями типа параметра цикла (при целочисленном значении параметра цикла шаг равен 1).

    В случае если тело цикла состоит более чем из одного оператора, необходимо использовать составной оператор:

    (рис 3.26) Алгоритм работы цикла for..do (рис 3.27) Представление цикла for...do с помощью блок-схемы
    for параметр_цикла:=начальное_значение to конечное_значение do
    	begin
    		оператор_1;
    		оператор_2;
    		...
    		оператор_N;
    	end;
    

    Опишем алгоритм работы цикла for..do (рис. 3.26).

  • Параметру_цикла присваивается начальное_значение.
  • Если значение параметра_цикла превосходит конечное_значение, то цикл завершает свою работу. В противном случае выполняется п. 3.
  • Выполняется оператор.
  • Значение параметра_цикла изменяется на соответствующий шаг и осуществляется переход к п. 2, и т. д.
  • Понятно, что этот алгоритм представляет собой цикл с предусловием.

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

    Фрагмент подпрограммы, приведённый далее, демонстрирует применение цикла for:

    var i : integer; c : char;
    	begin
    		{Вывод на экран чисел от 1 до 10.}
    		for i :=1 to 10 do
    			writeln ( i );
    		{Вывод на экран чисел от 10 до -10.}
    		for i :=10 downto -10 do
    			writeln ( i );
    		{Вывод на экран символов от a до r.}
    		for c := ’ a ’ to ’ r ’ do
    			writeln ( c );
    	end.
    

    Вернёмся к задаче вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1. Как видим, здесь количество повторений цикла явно не задано. Однако это значение, можно легко вычислить. Предположим, что параметр цикла $$x$$ принимает значения в диапазоне от $$x_n$$ до $$x_k$$, изменяясь с шагом $$dx$$, тогда количество повторений тела цикла можно определить по формуле:

    $$n = \round\left(\frac{x_k-x_n}{dx}\right)+1$$

    округлив результат деления до целого числа. Следовательно, фрагмент программы вывода значений функции $$y = e^{sin(x)}cos(x)$$ на отрезке $$[0; \pi]$$ с шагом 0.1 будет иметь вид:

    var i, n : integer; x, y : real;
    	begin
    	n:=round ( ( pi - 0)/0.1)+1; {Количество повторений цикла.}
    	x : = 0; {Начальное значение аргумента.}
    	{Цикл с известным числом повторений,}
    	{i - параметр цикла, изменяется от 1 до n с шагом 1.}
    	for i :=1 to n do
    	begin {Начало цикла.}
    		y:=exp ( sin ( x ) ) * cos ( x ); {Вычисление значения функции }
    		{для соответствующего значения аргумента.}
    		writeln ( ’ x= ’, x, ’    y= ’, y );
    		x:=x + 0.1; {Вычисление нового}
    		{значения аргумента.}
    	end; {Конец цикла.}
    end.
    

    3.5.4 Операторы передачи управления

    Операторы передачи управления принудительно изменяют порядок выполнения команд. В языке Free Pascal таких операторов пять: goto, break, continue, exit и halt.

    Оператор goto метка, где метка — обычный идентификатор, применяют для безусловного перехода, он передаёт управление оператору с меткой.

    метка : оператор;

    Обычно применение оператора goto приводит к усложнению программы и затрудняет отладку. Использование оператора нарушает принцип структурного программирования Как говорил учитель программирования авторов книги Владимир Андреевич Реуцкий: "Использование goto — признак дурного тона". , согласно которому все блоки, составляющие программу, должны иметь только один вход и один выход. В большинстве алгоритмов применения оператора goto можно избежать, в нашей книге мы будем его использовать, но читатель должен знать о наличии этого оператора в языке.

    Операторы break и continue используют только внутри циклов. Оператор break осуществляет немедленный выход из циклов repeat, while, for, и управление передается оператору, находящемуся непосредственно за циклом. Оператор continue начинает новую итерацию цикла, даже если предыдущая не была завершена.

    Оператор exit осуществляет выход из подпрограммы.

    Оператор halt прекращает выполнение программы.

    3.5.5 Решение задач с использованием циклов

    Рассмотрим использование циклических операторов на конкретных примерах.

    ЗАДАЧА 3.9. Найти наибольший общий делитель (НОД) двух натуральных чисел $$A$$ и $$B$$.

    Входные данные: A и B. Выходные данные: А — НОД.

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

    В блок-схеме решения задачи, представленной на рис. 3.28, для решения поставленной задачи используется цикл с предусловием, то есть тело цикла повторяется до тех пор, пока А не равно В. Следовательно, при создании программы воспользуемся циклом while..do.

    Поиск НОД для чисел A = 25 и B = 15.
    Исходные данные Первый шаг Второй шаг Третий шаг НОД(A,B)=5
    А=25 А=10 А=10 А=5
    В=15 В=15 В=5 В=5
    (рис 3.28) Алгоритм поиска наибольшего общего делителя двух чисел

    Программа на языке Free Pascal, реализующая поставленную задачу:

    var a, b : word;
    	begin
    	writeln ( ’введите два натуральных числа ’ );
    	write ( ’A= ’ ); readln ( a );
    	write ( ’B= ’ ); readln ( b );
    	{Если числа не равны, выполнять тело цикла.}
    	while a<>b do
    	{Если число А больше, чем В, то уменьшить его значение на В,}
    	if a>b then
    		a:=a-b
    	{иначе уменьшить значение числа В на А.}
    	else
    		b:=b-a;
    	writeln ( ’НОД= ’,A);
    end.
    
    (рис 3.29) Алгоритм вычисления факториала ЗАДАЧА 3.10. Вычислить факториал числа $$N(N!=1 \cdot 2 \cdot 3 \cdot \ldots \cdot N).$$.

    Входные данные: N — целое число, факториал которого необходимо вычислить.

    Выходные данные: factorial — значение факториала числа N, произведение чисел от 1 до N, целое число.

    Промежуточные переменные: i — параметр цикла, целочисленная переменная, последовательно принимающая значения 2, 3, 4 и так далее до N.

    Блок-схема приведена на рис. 3.29.

    Итак, вводится число N. Переменной factorial, предназначенной для хранения значения произведения последовательности чисел, присваивается начальное значение, равное единице. Затем организуется цикл, параметром которого выступает переменная i. Если значение параметра цикла меньше или равно N, то выполняется оператор тела цикла, в котором из участка памяти с именем factorial считывается предыдущее значение произведения, умножается на текущее значение параметра цикла, а результат снова помещается в участок памяти с именем factorial. Когда параметр i становится больше N, цикл заканчивается, и на печать выводится значение переменой factorial, которая была вычислена в теле цикла.

    Ниже представлен текст программы вычисления факториала на языке Free Pascal.

    var
    	factorial, n, i : integer;
    	begin
    		write ( ’ n= ’ ); readln ( n );
    		factorial : = 1;
    		for i :=2 to n do
    			factorial := factorial * i;
    		writeln ( factorial );
    	end.
    
    ЗАДАЧА 3.11. Вычислить a^n, где n — целое положительное число.

    Входные данные: a — вещественное число, которое необходимо возвести в целую положительную степень n. Выходные данные: p (вещественное число) — результат возведения вещественного числа a в целую положительную степень n. Промежуточные данные: i — целочисленная переменная, принимающая значения от 1 до n с шагом 1, параметр цикла. Блок-схема приведена на рис. 3.30.

    (рис 3.30) Алгоритм возведения вещественного числа в целую степень

    Известно, что для того, чтобы получить целую степень n числа a, нужно умножить его само на себя n раз. Результат этого умножения будет храниться в участке памяти с именем p. При выполнении очередного цикла из этого участка предыдущее значение будет считываться, умножаться на основание степени a и снова записываться в участок памяти p. Цикл выполняется n раз.

    В таблице 3.5 отображён протокол выполнения алгоритма при возведении числа 2 в пятую степень: a=2, n=5. Подобные таблицы, заполненные вручную, используются для тестирования — проверки всех этапов работы программы.

    Процесс возведения числа a в степень n
    i 1 2 3 4 5
    P 1 2 4 8 16 32

    Далее приведён текст программы, составленной для решения поставленной задачи.

    var
    	a, p : real;
    	i, n : word;
    	begin
    		write ( ’Введите основание степени a= ’ );
    		readln ( a );
    		write ( ’Введите показатель степени n= ’ );
    		readln ( n );
    		p : = 1;
    		for i :=1 to n do
    			p:=p * a;
    		writeln ( ’P= ’,P : 1 : 3 );
    	end.
    
    ЗАДАЧА 3.12. Вычислить сумму натуральных четных чисел, не превышающих $$N$$.

    Входные данные: N — целое число.

    Выходные данные: S — сумма четных чисел.

    Промежуточные переменные: i — параметр цикла, принимает значения 2, 4, 6, 8 и так далее, также имеет целочисленное значение.

    При сложении нескольких чисел необходимо накапливать результат в определённом участке памяти, каждый раз считывая из этого участка предыдущее значение суммы и прибавляя к нему следующее слагаемое. Для выполнения первого оператора накапливания суммы из участка памяти необходимо взять такое число, которое не влияло бы на результат сложения. Перед началом цикла переменной, предназначенной для накапливания суммы, необходимо присвоить значение нуль (s=0). Блок-схема решения этой задачи представлена на рис. 3.31.

    Так как параметр цикла i изменяется с шагом 2, в блок-схеме, построенной для решения данной задачи (рис. 3.31), использован цикл с предусловием, который реализуется при составлении программы с помощью оператора while..do:

    (рис 3.31) Алгоритм вычисления суммы чётных натуральных чисел
    var n, i, S : word;
    	begin
    	write ( ’ n= ’ );
    	readln ( n );
    	S : = 0;
    	i : = 2;
    	while i<=n do
    	begin
    		S:=S+i;
    		i := i +2;
    	end;
    	writeln ( ’ S= ’, S );
    end.
    

    Эту же задачу можно решить иначе, используя цикл for..do:

    var n, i, S : word;
    	begin
    		write ( ’ n= ’ );
    		readln ( n );
    		S : = 0;
    	for i :=1 to n do
    	{Если остаток от деления параметра цикла на 2 равен 0,}
    			{то это число четное, следовательно,}
    			{происходит накапливание суммы.}
    		if i mod 2 = 0 then
    			S:=S+i;
    	writeln ( ’ S= ’, S );
    end.
    

    В таблице 3.6 приведены результаты тестирования программы для n=7. Несложно заметить, что при нечётных значениях параметра цикла значение переменной, предназначенной для накапливания суммы, не изменяется.

    Суммирование чётных чисел
    i 1 2 3 4 5 6 7
    S 0 0 2 2 6 6 12 12
    ЗАДАЧА 3.13. Дано натуральное число $$N$$. Определить $$K$$ — количество делителей этого числа, меньших его (например, для $$N = 12$$ делители 1, 2, 3, 4, 6. Количество $$K = 5$$).

    Входные данные: N — целое число.

    Выходные данные: целое число K — количество делителей N.

    Промежуточные переменные: i — параметр цикла, возможные делители числа N.

    В блок-схеме, изображённой на рис. 3.32, реализован следующий алгоритм: в переменную K, предназначенную для подсчёта количества делителей заданного числа, помещается значение, которое не влияло бы на результат (k=0). Далее организовывается цикл, в котором изменяющийся параметр i выполняет роль возможных делителей числа N. Параметр цикла меняется от 1 до N/2 c шагом 1. Если заданное число делится нацело на параметр цикла, это означает, что i является делителем N, и значение переменной K следует увеличить на единицу. Цикл необходимо повторить N/2 раз.

    В таблице 3.7 отображены результаты тестирования алгоритма при определении делителей числа N=12.

    Определение количества делителей числа N
    i 1 2 3 4 5 6
    K 0 1 2 3 4 4 5
    (рис 3.32) Алгоритм подсчёта делителей натурального числа

    Текст программы, соответствующий описанному алгоритму:

    var N, i,K: word;
    	begin
    	write ( ’N= ’ );
    	readln (N);
    	K: = 0;
    	for i :=1 to N div 2 do
    		{Если N делится нацело на i, то}
    		if N mod i= 0 then
    			k:=K+1; {увеличить счетчик на единицу.}
    	writeln ( ’ K= ’,K);
    end.
    

    ЗАДАЧА 3.14. Дано натуральное число $$N$$. Определить, является ли оно простым. Натуральное число $$N$$ называется простым, если оно делится нацело без остатка только на единицу и $$N$$. Число 13 — простое, так как делится только на 1 и 13, N = 12 не является простым, так как делится на 1, 2, 3, 4, 6 и 12.

    Входные данные: N — целое число.

    Выходные данные: сообщение.

    Промежуточные данные: i — параметр цикла, возможные делители числа N.

    Алгоритм решения этой задачи (рис. 3.33) заключается в том, что необходимо определить, есть ли у числа N делители среди чисел от 2 до N/2. Если делителей нет — число простое. Предположим, что число N является простым (Pr:=true). Организуем цикл, в котором переменная i будет изменяться от 2 до N/2. В цикле будем проверять, делится ли N на i. Если делится, то мы нашли делитель, N не является простым (Pr:=false). Проверка остальных делителей не имеет смысла, аварийно покидаем цикл.

    (рис 3.33) Алгоритм определения простого числа

    В алгоритме предусмотрено два выхода из цикла. Первый — естественный, при исчерпании всех значений параметра, а второй — досрочный. После выхода из цикла надо проверить значение Pr. Если Pr=true, то число N — простое, иначе N не является простым числом.

    При составлении программы на языке Free Pascal досрочный выход из цикла удобно выполнять при помощи оператора break:

    var
    	N, i : integer;
    	Pr : boolean;
    	begin
    	write ( ’N= ’ );
    	readln (N);
    	Pr:= true; {Предположим, что число простое.}
    	for i :=2 to N div 2 do
    		{Если найдется хотя бы один делитель, то}
    		if N mod i = 0 then
    		begin
    			Pr:= false; {число простым не является и}
    			break; {досрочный выход из цикла.}
    		end;
    		{Проверка значения логического параметра, и}
    		if Pr then
    			{вывод на печать соответствующего сообщения.}
    			writeln ( ’Число  ’,N, ’ - простое ’ )
    		else
    			writeln ( ’Число  ’,N, ’  простым не является ’ );
    end.
    
    ЗАДАЧА 3.15. Определить количество простых чисел в интервале от $$N$$ до $$M$$, где $$N$$ и $$M$$ — натуральные числа, причем $$N \le M$$.

    Алгоритм решения данной задачи представлен на рис. 3.34.

    Обратите внимание, что здесь осуществляется проверка корректности ввода исходных данных. Если границы интервала не положительны, или значение N превышает M, ввод данных повторяется в цикле с постусловием до тех пор, пока не будут введены корректные исходные данные. Далее для каждого числа из указанного интервала (параметр i принимает значения от N до M) происходит проверка. Если число является простым, то переменная k увеличивается на единицу. Подробно определение простого числа описано в задаче 3.14.

    Программа на языке Free Pascal, реализующая алгоритм подсчёта количества простых чисел в заданном диапазоне:

    var N,M, i, j, k : longint;
    Pr : boolean;
    begin
    	repeat
    		write ( ’N= ’ );
    		readln (N);
    		write ( ’M= ’ );
    		readln (M);
    	until (N>0) and (M>0) and (N<M);
    	k : = 0; {Количество простых чисел.}
    	for i :=N to M do {Параметр i принимает значения от N до M.}
    	begin
    		{Определение простого числа.}
    		Pr:= true;
    		for j :=2 to i div 2 do
    			if i mod j = 0 then
    			begin
    				Pr:= false;
    				break;
    			end;
    			{Если число простое, увеличиваем количество на 1.}
    			if Pr then
    				k:=k+1;
    	end;
    	if k=0 then writeln ( ’Простых чисел в диапазоне нет ’ )
    	else writeln ( ’Простых чисел в диапазоне  ’, k );
    end.
    
    (рис 3.34) Алгоритм определения простых чисел в заданном интервале ЗАДАЧА 3.16. Дано натуральное число $$N$$. Определить количество цифр в числе.

    Входные данные: N — целое число.

    Выходные данные: kol — количество цифр в числе.

    Промежуточные данные: M — переменная для временного хранения значения N.

    Для того чтобы подсчитать количество цифр в числе, необходимо определить, сколько раз заданное число можно разделить на десять нацело. Например, пусть N=12345, тогда количество цифр kol = 5. Результаты вычислений сведены в таблицу 3.8. Алгоритм определения количества цифр в числе представлен на рис. 3.35.

    Определение количества цифр числа
    kol N
    1 12345
    2 12345 div 10=1234
    3 1234 div 10=123
    4 123 div 10=12
    5 12 div 10=1
    1 div 10=0

    Текст программы, реализующей данную задачу, можно записать так:

    var
    	M,N: longint;
    	kol : word;
    begin
    	{Так как речь идёт о натуральных числах,}
    	{при вводе предусмотрена проверка.}
    	{Закончить цикл, если введено положительное число,}
    	{иначе повторить ввод}
    	repeat
    		write ( ’N= ’ );
    		readln (N);
    	until N>0;
    	M:=N; {Сохранить значение переменной N. }
    	kol : = 1; {Пусть число состоит из одной цифры.}
    	while M div 10 > 0 do
    	{Выполнять тело цикла, пока число делится нацело на 10.}
    	begin
    		kol := kol +1; {Счётчик количества цифр.}
    		M:=M div 1 0; {Изменение числа.}
    	end;
    	writeln ( ’ kol= ’, kol );
    end.
    
    (рис 3.35) Алгоритм определения количества цифр в числе ЗАДАЧА 3.17. Дано натуральное число $$N$$. Определить, содержит ли это число нули и в каких разрядах они расположены (например, число 1 101 111 011 содержит ноль в третьем и восьмом разрядах).

    Входные данные: N — целое число.

    Выходные данные: pos — позиция цифры в числе.

    Промежуточные данные: i — параметр цикла, M — переменная для временного хранения значения N.

    В связи с тем, что разряды в числе выделяются, начиная с последнего, то для определения номера разряда в числе, необходимо знать количество цифр в числе12 Алгоритм нахождения количества цифр в числе был рассмотрен в предыдущей задаче. . Таким образом, на первом этапе решения задачи необходимо определить kol — количество цифр в числе. Затем начинаем выделять из числа цифры; если очередная цифра равна нулю, нужно вывести на экран номер разряда, который занимает эта цифра. Процесс определения текущей цифры числа N=120405 представлен в таблице 3.9.

    Блок-схема алгоритма решения данной задачи показана на рис. 3.36.

    (рис 3.36) Алгоритм решения задачи 3.17
    Определение текущей цифры числа
    i Число М Цифра Номер позиции
    1 120405 div 10=12040 120405 mod 10=5 6
    2 12040 div 10=1204 12040 mod 10=0 5
    3 1204 div 10=120 1204 mod 10=4 4
    4 120 div 10=12 120 mod 10=0 3
    5 12 div 10=1 12 mod 10=2 2
    6 1 div 10=0 1 mod 10=1 1

    Текст программы, реализующей данный алгоритм:

    var
    	M,N: longint;
    	i, pos, kol : word;
    begin
    	{Так как речь идет о натуральных числах,}
    	{при вводе предусмотрена проверка.}
    	{Закончить цикл, если введено положительное число,}
    	{иначе повторить ввод}
    	repeat
    		write ( ’N= ’ );
    		readln (N);
    	until N>0;
    	//Определение kol - количества разрядов.
    	M:=N; {Сохранить значение переменной N.}
    	kol : = 1; {Пусть число состоит из одной цифры.}
    	while M div 10 > 0 do
    	{Выполнять тело цикла, пока число делится нацело на 10.}
    	begin
    		kol := kol +1; {Счётчик количества цифр.}
    		M:=M div 1 0; {Изменение числа.}
    	end;
    	writeln ( ’ kol= ’, kol );
    	M:=N;
    	pos : = 0; {Пусть в числе нет нулей.}
    	for i :=1 to kol do
    	begin
    		{Выделение цифры из числа и сравнение её с нулем.}
    		if (M mod 10 = 0) then
    		begin
    		pos := kol-i +1; {Позиция нуля в числе.}
    		writeln ( ’Ноль в  ’, pos, ’м- разряде. ’ );
    		end;
    		M:=M div 1 0; {Изменение числа.}
    	end;
    	if pos=0 then writeln ( ’Число не содержит цифру 0. ’ );
    end.
    

    3.5.6 Ввод данных из диалогового окна в среде Lazarus

    Окно ввода — это стандартное диалоговое окно, которое появляется на экране в результате вызова функции InputBox. В общем виде оператор ввода данных с использованием этой функции записывают так:

    имя:= InputBox (заголовок_окна,подсказка,значение );

    где

  • заголовок_окна — строка, определяющая название окна;
  • подсказка — текст поясняющего сообщения;
  • значение — строка, которая будет находиться в поле ввода при появлении окна на экране;
  • имя — переменная строкового типа, которой будет присвоено значение из поля ввода.
  • После выполнения фрагмента программы

    var S : string;
    begin
    	S:= InputBox ( ’ЗАГОЛОВОК ОКНА ’,
    		’Подсказка :  введите исходные данные ’, ’Данное значение ’ );
    end;
    

    появится окно, представленное на рис. 3.37.

    (рис 3.37) Окно ввода

    У пользователя есть возможность изменять текст в поле ввода. Щелчок по кнопке ОК приведёт к тому, что в переменную, указанную слева от оператора присваивания, будет занесена строка, находящаяся в поле ввода. В данном случае в переменную S будет записана строка ’Данное значение’. Щелчок по кнопке Cancel закроет окно ввода.

    Учитывая, что функция InputBox возвращает строковое значение, при вводе числовых данных применяют функции преобразования типов:

    var S : string; gradus, radian : real;
    begin
    	S:= InputBox ( ’Ввод данных ’,
    			’Введите величину угла в радианах ’, ’ 0,000 ’ );
    	gradus := StrToFloat ( S );
    	radian := gradus * pi / 1 8 0;
    	MessageDlg ( ’Величина угла в градусах ’
    			+FloatToStr ( radian ), MtInformation, [ mbOk ], 0 );
    end;
    

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

    ЗАДАЧА 3.18. Поступает последовательность из $$N$$ вещественных чисел. Определить наибольший элемент последовательности.

    Входные данные: N — целое число; Х — вещественное число, определяет текущий элемент последовательности.

    Выходные данные: Max — вещественное число, элемент последовательности с наибольшим значением.

    Промежуточные переменные: i — параметр цикла, номер вводимого элемента последовательности.

    Алгоритм поиска наибольшего элемента в последовательности следующий (рис. 3.38).

    В памяти компьютера отводится ячейка, например, с именем Max, в которой будет храниться наибольший элемент последовательности — максимум. Вводим количество элементов последовательности и первый элемент последовательности. Предполагаем, что первый элемент последовательности наибольший, и записываем его в Max. Затем вводится второй элемент последовательности и сравнивается с предполагаемым максимумом. Если окажется, что второй элемент больше, его записываем в ячейку Max. В противном случае никаких действий не предпринимаем. Потом переходим к вводу следующего элемента последовательности, и алгоритм повторяется с начала. В результате, в ячейке Max будет храниться элемент последовательности с наибольшим значением Для поиска наименьшего элемента последовательности (минимума), предполагают, что первый элемент — наименьший, записывают его в ячейку Min, а затем среди элементов последовательности ищут число, значение которого будет меньше, чем предполагаемый минимум .

    Разместим на форме объект типа надпись Label1 и кнопку Button1 (см. рисунок 3.39). Щелчок по кнопке приведёт к выполнению следующей процедуры:

    (рис 3.38) Алгоритм поиска наибольшего числа в последовательности
    procedure TForm1. Button1Click ( Sender : TObject );
    var i,N: integer; max,X: real; S : string; begin
    	//Ввод количества элементов последовательности.
    	S:= InputBox ( ’Ввод ’,
    	’Введите_количество элементов в последовательности. ’, ’ 0 ’ );
    	N:= StrToFloat ( S );
    	//Ввод первого элемента последовательности.
    	S:= InputBox ( ’Ввод элементов последовательности ’,
    			’Введите число. ’, ’ 0 ’ );
    	X:= StrToFloat ( S );
    	//Предположим, что первый элемент максимальный — Max=X.
    	max:=X;
    	//Параметр цикла принимает стартовое значение i=2,
    	//т.к. первый элемент уже введён.
    	for i :=2 to N do
    	begin
    		//Ввод следующих элементов последовательности.
    		S:= InputBox ( ’Ввод элементов последовательности ’,
    		’Введите число. ’, ’ 0 ’ );
    		X:= StrToInt ( S );
    		//Если найдётся элемент, превышающий максимум,
    		//записать его в ячейку Max —
    		//теперь он предполагаемый максимум.
    		if X>max then max:=X;
    	end;
    	//Вывод наибольшего элемента последовательности.
    	MessageDlg ( ’Значение наибольшего элемента -  ’
    		+FloatToStr (max ), MtInformation, [ mbOk ], 0 );
    end;
    

    Результаты работы программы представлены на рис. 3.39,3.40,3.41,3.42.

    (рис 3.39) Первое окно диалога к задаче 3.18 (рис 3.40) Второе окно диалога к задаче 3.18 (рис 3.41) Третье окно диалога к задаче 3.18 (рис 3.42) Результат работы программы к задаче 3.18 ЗАДАЧА 3.19. Вводится последовательность целых чисел, 0 — конец последовательности. Найти наименьшее число среди положительных, если таких значений несколько, определить, сколько их.

    Блок-схема решения задачи приведена на рис. 3.43.

    (рис 3.43) Алгоритм поиска минимального положительного числа в последовательности

    Далее приведён текст подпрограммы с подробными комментариями Алгоритм поиска максимального (минимального) элементов последовательности подробно описан в задаче 3.18. . Подпрограмма выполняется при обращении к кнопке Button1, предварительно размещённой на форме.

    procedure TForm1. Button1Click ( Sender : TObject );
    	var N, k, min : integer; S : string;
    	begin
    	//Ввод первого элемента последовательности.
    	S:= InputBox ( ’Ввод элементов последовательности ’,
    		’введите_число.  0 - конец последовательности ’, ’ 0 ’ );
    	N:= StrToInt ( S );
    //Предположим, что положительных чисел нет — K=0. В переменной
    //K будет храниться количество минимумов среди положительных
    //чисел.
    	k : = 0;
    	//Пока введённое число не равно нулю, выполнять тело цикла.
    	while N<>0 do
    	begin
    		//Проверяем, является ли введённое число положительным.
    		if N>0 then
    		begin
    //Если N>0 и K=0, поступил 1-й положительный элемент,
    //предположим, что он минимальный Min=N, соответственно,
    //количество минимумов равно 1.
    		if k=0 then
    		begin
    			k : = 1;
    			min:=N;
    		end
    		//Если элемент не первый, сравниваем его
    		//с предполагаемым минимумом;
    		//если элемент меньше, записываем его
    		//в Min и обнуляем счетчик.
    		else if N<min then
    			begin
    				min:=N;
    				k : = 1;
    			end
    //Если элемент равен минимуму,
    		//увеличиваем количество минимумов на 1.
    		else if N=min then k:=k+1;
    	end;
    		//Ввод следующего элемента последовательности.
    		S:= InputBox ( ’Ввод элементов последовательности ’,
    			’введите число.  0 - конец последовательности ’, ’ 0 ’ );
    		N:= StrToInt ( S );
    	end; //Конец цикла.
    	if k<>0 then
    		//Если значение счётчика не равно нулю,
    		//выводим значение минимального элемента и
    		//количество таких элементов,
    		MessageDlg ( ’MIN =  ’+IntToStr ( min)+
    			’  K= ’+IntToStr ( k ), MtInformation, [ mbOk], 0 )
    	else
    		//в противном случае сообщаем,
    		//что положительных чисел нет.
    		MessageDlg ( ’Положительных чисел нет ’,
    			MtInformation, [ mbOk ], 0 );
    end;
    
    ЗАДАЧА 3.20. Определить, сколько раз последовательность из $$N$$ произвольных чисел меняет знак.

    Чтобы решить задачу, нужно попарно перемножать элементы последовательности. Если результат произведения пары чисел — отрицательное число, значит, эти числа имеют разные знаки.

    Пусть $$k$$ — количество смен знака последовательности — равно 0.

    Пусть в переменной А хранится текущий элемент последовательности, а в переменой В — предыдущий. Введём N — количество элементов последовательности. Организуем цикл (переменная i меняется от 1 до N). В цикле будем делать следующее: вводим очередной элемент последовательности (A); если это это первый элемент последовательности (i=1), то сравнивать его не с чем, и просто переписываем переменную A в переменную B (B:=A). Если это не первый последовательности ($$i \not = 1$$), то проверяем знак произведения $$A \cdot B$$ (текущего и предыдущего элемента последовательности). Если произведение отрицательно, то счётчик k увеличиваем на 1. После чего не забываем в переменную B записать A. Блок-схема алгоритма приведена на рис. 3.44.

    Разметим на форме два объекта типа надпись Label1 и Label2, объект поле ввода Edit1 и кнопку Button1 (рис. 3.45).

    Текст подпрограммы, которая будет выполнена при обращении к кнопке, приведён далее.

    (рис 3.44) Алгоритм к задаче 3.20 (рис 3.45) Форма программы решения задачи 3.20
    procedure TForm1. Button1Click ( Sender : TObject );
    var N, A, B, i, k : integer; S : string;
    begin
    	N:= StrToInt ( Edit1. Text );
    	S:= InputBox ( ’Ввод элементов последовательности ’,
    		’Введите число.  0 - конец_последовательности ’, ’ 0 ’ );
    	A:= StrToInt ( S );
    	k : = 0;
    	for i :=1 to N do
    	begin
    		S:= InputBox ( ’Ввод элементов последовательности ’,
    			’введите число. 0 - конец последовательности ’, ’ 0 ’ );
    		A:= StrToInt ( S );
    		if ( i <>1) then
    		if A*B<0 then k:=k+1;
    		B:=A;
    	end;
    	MessageDlg ( ’K =  ’+IntToStr ( k ), MtInformation, [ mbOk ], 0 ); end;
    

    Результаты работы программы представлены на рис. 3.46, 3.47.

    (рис 3.46) Второе окно диалога к задаче 3.20 (рис 3.47) Результат работы программы к задаче 3.20
    Вернуться к учебному плану