«Free Pascal и Lazarus Учебник по программированию Е. Р. Алексеев О. В. Чеснокова Т. В. Кучер Москва ALT Linux; Издательский дом ДМК-пресс 2010 УДК 004.432 ББК 22.1 А47 Алексеев Е. Р., Чеснокова О. В., Кучер Т. В. А47 ...»
При возведении числа x в отрицательную степень следует помнить, что Таким образом, для программирования выражения, содержащего возведение в степень, надо внимательно проанализировать значения, которые могут принимать x и n, так как в некоторых случаях возведение x в степень n невыполнимо.
Некоторые функции, предназначенные для работы со строками, представлены в табл. 2.7.
concat(S1, S2,...) delete(S, n, m) insert(S, n, m) pos(S1, S2) 9 Формула формируется следующим образом: логарифмируем выражение xn, получается n ln(x), затем экспоненцируем последнее.
72 Глава 2. Общие сведения о языке программирования Free Pascal DataTimeToStr(V) Поясним назначение функции FloatToStrF(V,F,P,D). Обычно её используют для форматированного вывода вещественного числа. Значения параметра F представлены в табл. 2.8, P количество символов в формируемой из числа строке, D количество символов в дробной части числа.
Таблица 2.8. Параметры функции FloatToStrF Экспоненциальная форма представления числа, P мантисса, D ffExponent Число в формате с фиксированной точкой, P общее количество ffFixed цифр в представлении числа, D количество цифр в дробной части:
Универсальный формат, использует наиболее удобную форму предffGtneral Число в формате с фиксированной точкой, использует символ разffNumber делителя тысяч при выводе больших чисел.
Денежный формат, соответствует ffNumber, но в конце ставит символ ffCurency Примером работы функции FloatToStrF служит фрагмент программы:
var n : integer ;
St : string ;
begin n:=5;
St := ’ Иванов А. ’ ;
//Выражение chr(13) символ окончания строки.
//Для вывода вещественного числа m отводится 4 позиции //вместе с точкой, две позиции после точки.
Label1. Caption := ’ Студент ’+St+ ’ сдал ’+I n t T o S t r ( n)+ ’ экзаменов. ’+chr (13)+ ’ Средний балл составил ’+ End ;
Его результатом будет фраза:
Студент Иванов А. сдал 5 экзаменов.
Средний балл составил 4.80.
В табл. 2.9 приведены функции, предназначенные для работы с датой и временем.
Если в процессе работы над программой возникает необходимость в создании переменной, размер которой неизвестен заранее, используют динамическую память 10. В языке Free Pascal операции распределения памяти осуществляются по средством представленными в табл. 2.10.
10 Динамическая память это область памяти, которая в процессе компиляции отводится для хранения временных данных.
74 Глава 2. Общие сведения о языке программирования Free Pascal Таблица 2.9. Функции для работы с датой и временем date time adr(x) Возвращает фрагмент динамической памяти, который ранее dispose(р) Возвращает фрагмент динамической памяти, который ранее Freemen(p,size) Резервирует за нетипизированным указателем p фрагмент диGetMem(p,size) Резервирует фрагмент динамической памяти для размещения New(p) Возвращает длину в байтах внутреннего представления укаSizeOf(x) С подробным описанием приведённых в этой главе функций и множеством других функций можно ознакомиться в справочной системе Lazarus.
Рассмотрим решение задачи с использованием стандартных функций.
ЗАДАЧА 2.1. Известны длины сторон треугольника a, b иc. Вычислить площадь S, периметр P и величины углов, и треугольника (рис. 2.6).
Прежде чем приступить к написанию программы, вспомним математические формулы, необходимые для решения задачи. Для вычисления площади треугольника применим теорему Герона: S = r(r a)(r b)(r c), где r = = (a + b + c)/2 полупериметр; один из углов найдём по теореме косинусов:
Таблица 2.11. Заголовки компонентов формы, представленной на рис. 2. cos() = b2 + c2 a2 /(2 · b · c); второй по теореме синусов: sin() = b/a · sin();
третий по формуле: = ( + ).
Решение задачи можно разбить на следующие этапы:
1) Определение значений a, b и c (ввод величин a, b и c в память компьютера).
2) Расчёт значений S, P,, и по приведённым выше формулам.
3) Вывод значений S, P,, и.
Процесс разработки несложного программного интерфейса описан в главе 1.
Попытайтесь самостоятельно разработать внешний вид данной программы. Разместите на форме десять меток, три поля ввода и одну кнопку. Измените заголовки формы и меток (свойство Caption) в соответствии с табл. 2.11. В качестве свойства Text компонентов Edit1, Edit2, Edit3 установите пробел. В результате форма должна выглядеть так, как показано на рис. 2.7.
76 Глава 2. Общие сведения о языке программирования Free Pascal Итак, проект формы готов. В окне программного кода среды Lazarus автоматически сформировал структуру модуля, перечислив названия основных разделов. Двойной щелчок по кнопке Вычислить приведёт к созданию процедуры TForm1.Button1Click в разделе implementation:
procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
begin end ;
и её описанию в разделе interface. Понятно, что созданная процедура не содержит ни одной команды. Задача программиста заполнить шаблон описаниями и операторами. Все команды, указанные в процедуре между словами begin и end, будут выполнены при щелчке по кнопке Выполнить. В нашем случае процедура TForm1.Button1Click будет иметь вид:
procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
//Описание переменных:
// a, b, с - стороны треугольника;
// alfa, beta, gamma - углы треугольника;
// S - площадь треугольника;
// r - полупериметр треугольника //Все переменные вещественного типа.
begin //Из полей ввода Edit1, Edit2, Edit3 считываются введенные строки, //с помощью функции StrToFloat(х) преобразовываются //в вещественные числа и записываются в переменные а, b, c.
//Вычисление значения полупериметра.
r :=( a+b+c ) / 2 ;
//Вычисление значения площади, //для вычисления применяется функция:
// sqrt(х) - корень квадратный из х.
//Вычисление значения угла alfa в радианах.
//Для вычисления применяем функции:
a l f a := arccos ( ( sqr ( b)+sqr ( c)sqr ( a ) ) / 2 / b/ c ) ;
//Вычисление значения угла beta в радианах.
//Для вычисления применяем функции:
//Вычисление значения угла gamma в радианах.
//Математическая постоянная определена функцией без аргумента pi.
gamma:= pi ( a l f a+b e t a ) ;
//Перевод из радиан в градусы.
gamma:=gamma180/ p i ;
//Для вывода результатов вычислений используем //операцию слияния строк + и функцию FloatToStrF(х), //которая преобразовывает вещественную переменную х в строку //и выводит ее в указанном формате, в нашем случае //под переменную отводится три позиции, включая точку //и ноль позиций после точки.
//Величины углов в градусах выводятся на форму //в соответствующие объекты типа надпись.
Label6. Caption := ’ a l f a= ’+FloatToStrF ( a l f a, f f F i x e d, 3, 0 ) ;
Label7. Caption := ’ b e t a= ’+FloatToStrF ( beta, f f F i x e d, 3, 0 ) ;
Label8. Caption := ’gamma= ’+FloatToStrF (gamma, f f F i x e d, 3, 0 ) ;
//Используем функцию FloatToStrF(х) для форматированного //вывода, в нашем случае под все число отводится пять позиции, //включая точку, и две позиций после точки.
//Значения площади и периметра выводятся на форму.
Label9. Caption := ’ Периметр P= ’+FloatToStrF ( 2 r, f f F i x e d, 5, 2 ) ;
Label10. Caption := ’ Площадь S= ’+FloatToStrF ( S, f f F i x e d, 5, 2 ) ;
end ;
Обратите внимание, что были написаны всего десять команд, предназначенных для решения поставленной задачи. Остальной текст в окне редактора создаётся автоматически. В результате весь программный код (без комментариев) имеет вид:
unit Unit1 ;
{$mode objfpc}{$H+} interface uses type { TForm1 } TForm1 = c l a s s (TForm) 78 Глава 2. Общие сведения о языке программирования Free Pascal procedure B u t t o n 1 C l i c k ( Sender : TObject ) ;
private { private declarations } public { public declarations } var Form1 : TForm1 ;
implementation { TForm1 } procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
begin gamma:=gamma180/ p i ;
Label8. Caption := ’gamma= ’+FloatToStrF (gamma, f f F i x e d, 3, 0 ) ;
Label9. Caption := ’ Периметр P= ’+ Label10. Caption := ’ Площадь S= ’+FloatToStrF ( S, f f F i x e d, 5, 2 ) ;
initialization {$I unit1.lrs} end.
Рис. 2.8. Результат работы программы к задаче 2. На рис. 2.8 представлено диалоговое окно, которое появится, если запустить эту программу, щёлкнув по кнопке Вычислить.
Теперь напишем консольное приложение для решения этой задачи. Для этого запустим текстовый редактор Geany. Выполним команду Файл New with Template Pascal Source File. В открывшемся окне реактора введём следующий текст программы.
program pr3 ;
//Подключение модуля для работы с математическими функциями, //см. табл. 2.6.
uses math ;
//Описание всех используемых переменных.
BEGIN //Ввод сторон трегольника.
writeln ( ’ Введите стороны треугольника ’ ) ;
readln ( a, b, c ) ;
//Вычисление значения полупериметра.
R:=( a+b+c ) / 2 ;
//Вычисление значения площади, //для вычисления применяется функция:
// sqrt(х) корень квадратный из х.
//Вычисление значения угла alfa в радианах.
//Для вычисления применяем функции:
80 Глава 2. Общие сведения о языке программирования Free Pascal //Вычисление значения угла beta в радианах.
//Для вычисления применяем функции:
//Вычисление значения угла gamma в радианах.
//Математическая постоянная определена //функцией без аргумента pi.
gamma:= pi ( a l f a+b e t a ) ;
//Перевод из радиан в градусы.
gamma:=gamma180/ p i ;
//Вывод площади и сторон треугольника.
writeln ( ’ Площадь треугольника = ’, S : 6 : 2 ) ;
END.
Для компиляции программы в Geany необходимо выполнить команду Построить Собрать (F8), для запуска Построить Выполнить (F5).
На рис. 2.9 представлены результаты работы программы.
Введите стороны треугольника Площадь треугольника = 6. alf= 36.87 bet= 53.13 gam= 90. Рис. 2.9. Результаты работы консольного приложения решения задачи 2. 2.7 Задачи для самостоятельного решения Разработать консольное приложение и программу в среде программирования Lazarus. Для каждой задачи создать интерфейс, соответствующий условию.
1) Заданы два катета прямоугольного треугольника. Найти гипотенузу и углы треугольника.
2) Известны гипотенуза и прилежащий угол прямоугольного треугольника.
Найти площадь треугольника.
3) Известна диагональ квадрата d. Вычислить площадь S и периметр P квадрата.
4) Известны диагональ прямоугольника d и угол между диагональю и большей стороной. Вычислить площадь S прямоугольника.
5) Треугольник задан величинами своих сторон a, b, c. Найти углы треугольника,,.
6) Тело имеет форму параллелепипеда с высотой h. Прямоугольник в основании имеет диагональ d. Известно, что диагонали основания пересекаются под углом. Найти объём тела V и площадь поверхности S.
7) В треугольнике известны катет a и площадь S. Найти величину гипотенузы c, второго катета b и углов и.
8) Известна площадь квадрата S. Вычислить сторону квадрата a, диагональ d и площадь S1 описанного вокруг квадрата круга.
9) В равнобедренном треугольнике известно основание c, угол при основании равен. Найти площадь треугольника S и величину боковой стороны a.
10) Известны координаты трёх вершин прямоугольника ABCD: A(x1, y1), B(x2, y2) и C(x3, y3). Найти его площадь и периметр.
11) Заданы два катета прямоугольного треугольника. Вычислить его площадь и периметр.
12) Известны катет c и противолежащий угол прямоугольного треугольника.
Найти периметр треугольника.
13) Известна диагональ квадрата d. Вычислить площадь вписанной в него окружности S.
14) Известны длина диагоналей прямоугольника d и угол между ними. Вычислить площадь S прямоугольника.
15) В прямоугольном треугольнике известны катет b и площадь S. Вычислить периметр треугольника.
16) Известны координаты вершин квадрата ABCD: A(x1, y1) и C(x2, y2). Найти его площадь и периметр.
17) В прямоугольном треугольнике известны катет b и площадь S. Найти величину гипотенузы c, второго катета a и углов и.
18) Известно значение периметра P равностороннего треугольника. Вычислить его площадь.
19) Задан периметр квадрата P. Вычислить сторону квадрата a, диагональ d 20) В равнобедренном треугольнике известны основание c и высота h. Найти площадь треугольника S и периметр P.
21) Известны координаты вершин треугольника ABC: A(x1, y1), B(x2, y2) и C(x3, y3). Найти его площадь и периметр.
22) Металлический слиток имеет форму цилиндра, площадь поверхности S, высота h, плотность. Вычислить массу m слитка.
23) Задан первый член и разность арифметической прогрессии. Вычислить сумму n членов арифметической прогрессии и значение n-го члена.
24) Задан первый член и знаменатель геометрической прогрессии. Вычислить сумму n членов геометрической прогрессии и значение n-го члена.
25) Тело падает с высоты h. Какова его скорость в момент соприкосновения с землёй и когда это произойдёт.
Глава Операторы управления В этой главе изложена методика составления алгоритмов с помощью блоксхем и описаны основные операторы языка: условный оператор if, оператор выбора case, операторы цикла while..do, repeat..until, for..do. Приводится большое количество примеров составления программ различной сложности.
3.1 Основные конструкции алгоритма Как правило, созданию программы предшествует разработка алгоритма1. Алгоритм это чёткое описание последовательности действий, которые необходимо выполнить для того, чтобы при соответствующих исходных данных получить требуемый результат. Одним из способов представления алгоритма является блок-схема. При составлении блок-схемы все этапы решения задачи изображаются с помощью различных геометрических фигур. Эти фигуры называют блоками и, как правило, сопровождают текстом. Последовательность выполнения этапов указывают при помощи стрелок, соединяющих эти блоки. Типичные этапы решения задачи изображаются следующими геометрическими фигурами:
• блок начала (конца) (рис. 3.1). Текст внутри блока: начало ( конец );
• блок ввода (вывода) данных (рис. 3.2). Текст внутри блока: ввод ( вывод или печать ) и список вводимых (выводимых) переменных;
1 Алгоритм от algorithmi, algorismus, первоначально латинская транслитерация имени математика аль-Хорезми.
• блок решения, или арифметический (рис. 3.3). Внутри блока записывается действие, вычислительная операция или группа операций;
• условный блок (рис. 3.4). Логическое условие записывается внутри блока.
В результате проверки условия осуществляется выбор одного из возможных путей (ветвей) вычислительного процесса.
Рассмотренные блоки позволяют описать три основные конструкции алгоритма: линейный процесс, разветвляющийся процесс и циклический процесс.
Линейный процесс это конструкция, представляющая собой последовательное выполнение двух или более блоков (рис. 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 При решении большинства задач порядок вычислений зависит от определённых условий, например от исходных данных или от промежуточных результатов, полученных на предыдущих шагах программы. Для организации вычислений Рис. 3.8. Алгоритм условного оператора if..then..else в зависимости от какого-либо условия в языке Free Pascal используется условный оператор if..then..else, который в общем виде записывается так:
i f условие then оператор_1 e l s e оператор_2 ;
где if..then..else зарезервированные слова, условие выражение логического типа2, оператор_1 и оператор_2 любые операторы языка Free Pascal.
Работа условного оператора организована следующим образом. Сначала вычисляется выражение, записанное в условии. Если оно имеет значение истина (True), то выполняется оператор_1. В противном случае, когда выражение имеет значение ложь (False), оператор_1 игнорируется и управление передаётся оператору_2.
Алгоритм, который реализован в условном операторе if..then..else, представлен на рис. 3.8.
Например, чтобы сравнить значения переменных x и y, нужно создать следующий программный код:
write ( ’ x= ’ ) ; readln ( x ) ;
write ( ’ y= ’ ) ; readln ( y ) ;
i f x=y then writeln ( ’ значение x равно значению y ’ ) writeln ( ’ значение x не равно значению y ’ ) ;
else Если в задаче требуется, чтобы в зависимости от значения условия выполнялся не один оператор, а несколько, необходимо использовать составной оператор:
i f условие then begin оператор_1 ;
2 Логическое выражение может принимать одно из двух значений: истина или ложь.
оператор_2 ;
оператор_n ;
else begin оператор_1 ;
оператор_2 ;
оператор_n ;
Альтернативная ветвь else в условном операторе может отсутствовать, если в ней нет необходимости:
i f условие then оператор ;
или i f условие then begin оператор_1 ;
оператор_2 ;
оператор_n ;
В таком усечённом виде условный оператор работает так: оператор (группа операторов) либо выполняется, либо пропускается в зависимости от значения выражения, представляющего условие. Алгоритм этого условного процесса представлен на рис. 3.9.
Пример применения условного оператора, без альтернативной ветви else может быть таким:
write ( ’ x= ’ ) ; readln ( x ) ;
write ( ’ y= ’ ) ; readln ( y ) ;
c :=0;
{Значение переменной c изменяется только при условии,} {что x не равно y.} i f ( xy ) then c :=x+y ;
{Вывод на экран значения переменной c} {выполняется в любом случае.} writeln ( ’ Значение переменной с= ’, c ) ;
Условные операторы могут быть вложены друг в друга. При вложениях условных операторов всегда действует правило: альтернатива else считается принадлежащей ближайшему if, имеющему ветвь else. Например, в записи Рис. 3.9. Алгоритм условного оператора if без альтернативной ветви else i f условие_1 then i f условие_2 then e l s e оператор_Б ;
оператор_Б относится к условию_2, а в конструкции i f условие_1 then begin e l s e оператор_Б ;
он принадлежит оператору if с условием_1.
Для сравнения переменных в условных выражениях применяют операции отношения: =,,, =. Условные выражения составляют с использованием логических операций and, or и not. В языке Free Pascal приоритет операций отношения меньше, чем у логических операций, поэтому составные части сложного логического выражения заключают в скобки.
Допустим, нужно проверить, принадлежит ли переменная x интервалу [a, b].
Условный оператор будет иметь вид:
i f ( x>=a ) and ( x=a and x=(a and x)=1) and ( x=2) and ( y=0 ложно, то end.
3.4.2 Оператор варианта case Оператор варианта case необходим в тех случаях, когда в зависимости от значений какой-либо переменной надо выполнить те или иные операторы.
case выражение of значение_1 : оператор_1 ;
значение_2 : оператор_2 ;
значение_N : оператор_N else альтернативный_оператор end ;
Здесь выражение переменная перечислимого типа (включая char и boolean), значение_1, значение_2,..., значение_N это конкретное значение управляющей переменной или выражение, при котором необходимо выполнить соответствующий оператор, игнорируя остальные варианты. Значения в каждом наборе должны быть уникальны, то есть они могут появляться только в одном варианте.
Пересечение наборов значений для разных вариантов является ошибкой.
Оператор работает следующим образом. Вычисляется значение выражения.
Затем выполняется оператор, помеченный значением, совпадающим со значением выражения. То есть, если выражение принимает значение_1, то выполняется оператор_1. Если выражение принимает значение_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 ( ’ ВТОРНИК ’ ) ;
4 : writeln ( ’ ЧЕТВЕРГ ’ ) ;
5 : writeln ( ’ ПЯТНИЦА ’ ) ;
end.
В предложенной записи оператора варианта отсутствует ветвь else. Это объясняется тем, что переменная R может принимать только одно из указанных значений, т. е. 1, 2, 3, 4, 5, 6 или 0.
ЗАДАЧА 3.8. По заданному номеру месяца m вывести на печать название времени года.
Для решения данной задачи необходимо проверить выполнение четырёх условий. Если заданное число m равно 12, 1 или 2, то это зима, если m попадает в диаГлава 3. Операторы управления пазон от 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 на печать //выводится название времени года.
//Если значение переменной m выходит за пределы области //допустимых значений, то выдается сообщение об ошибке.
e l s e writeln ( ’ ОШИБКА ПРИ ВВОДЕ ! ! ! ’ ) ;
end.
3.4.3 Обработка ошибок. Вывод сообщений в среде Lazarus Понятно, что чем меньше в программе ошибок, тем она лучше. В очень хорошей программе ошибок нет вообще. А это значит, что программист должен не только основательно продумать алгоритм поставленной задачи, но и предугадать ошибки, которые может допустить пользователь, работая с программой.
Если пользователь допустил ошибку, например, при вводе данных, его необходимо проинформировать об этом. Для этого можно воспользоваться функцией MessageDlg, которая выводит сообщение в отдельном окне. В общем виде функцию записывают так:
MessageDlg ( сообщение, тип_сообщения, [ список_кнопок ], справка ) ;
где • сообщение текст, который будет отображён в окне сообщения;
• тип_сообщения определяет внешний вид окна (табл. 3.2);
• список_кнопок константы (перечисляются через запятую), определяющие тип кнопок окна сообщения (табл. 3.3);
• справка номер окна справочной системы, которое будет выведено на экран, если нажать F1; параметр равен нулю, если использование справки не предусмотрено.
Таблица 3.2. Тип окна сообщения Рис. 3.19. Конструирование формы для решения квадратного уравнения Вернёмся к задаче решения квадратного уравнения (задача 3.3). Нами был рассмотрен алгоритм решения этой задачи и написана программа на языке программирования Free Pascal. Реализуем эту задачу в среде Lazarus.
Создадим новый проект6 (рис. 3.19).
Для организации ввода коэффициентов уравнения внедрим на форму четыре объекта типа надпись (Label1, Label2, Label3, Label4) и три поля ввода (Edit1, Edit2, Edit3). Корни уравнения или сообщение об их отсутствии будем выводить в надпись Label57. Все действия по вычислению корней квадратного уравнения будут выполняться при нажатии кнопки Button1.
При вводе данных в программе могут возникнуть следующие ошибки:
• в поле ввода оказалась строка, которую невозможно преобразовать в число;
• значение коэффициента a равно нулю8.
Для того чтобы не допустить подобные ошибки, необходимо контролировать данные, вводимые пользователем. Применим для этой цели встроенную процедуру Val(S,X,Kod), которая преобразовывает строку S в число. Тип этого числа (целое или вещественное) зависит от типа переменной X. Если преобразование 6 Подробно о создании проекта см. в главе 1.
7 На этапе конструирования формы сделаем метку Label5 невидимой, для чего свойство Visible установим в false (Label5.Visible:=false).
8 При вычислении корней произойдёт деление на ноль.
прошло успешно, то параметр Kod принимает значение, равное нулю, а результат преобразования записывается в переменную X. В противном случае Kod содержит номер позиции в строке S, где произошла ошибка, и содержимое переменной X не меняется. Далее приведён фрагмент программы с подробными комментариями:
procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
var kod1, kod2, kod3 : integer ;
begin //Ввод значений коэффициентов квадратного уравнения.
//Из поля ввода Edit1 считывается строка символов и //преобразовывается в вещественное число, //если преобразование прошло успешно, то kod1=0 и //полученное число записывается в переменную a.
//Если преобразования прошли успешно, то i f ( kod1=0) and ( kod2=0) and ( kod3=0) then //проверить чему равен первый коэффициент.
//Если значение первого коэффициента равно нулю, то MessageDlg ( ’ Введите не нулевое значение а ’, {иначе уменьшить значение числа В на А.} else writeln ( ’ НОД= ’,A ) ;
end.
Рис. 3.29. Алгоритм вычисления факториала ЗАДАЧА 3.10. Вычислить факториал числа N (N ! = 1 · 2 · 3 ·... · 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 begin Входные данные: 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. Подобные таблицы, заполненные вручную, используются для тестирования проверки всех этапов работы программы.
Таблица 3.5. Процесс возведения числа a в степень n Далее приведён текст программы, составленной для решения поставленной задачи.
var begin write ( ’ Введите основание степени a= ’ ) ;
write ( ’ Введите показатель степени n= ’ ) ;
ЗАДАЧА 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 while i0) and (M>0) and (N< ;M) k :=0;
f or i :=N to M do {Параметр i принимает значения от N до M.} begin Рис. 3.34. Алгоритм определения простых чисел в заданном интервале {Определение простого числа.} {Если число простое, увеличиваем количество на 1.} i f k=0 then writeln ( ’ Простых чисел в диапазоне нет ’ ) e l s e writeln ( ’ Простых чисел в диапазоне ’, k ) ;
end.
ЗАДАЧА 3.16. Дано натуральное число N. Определить количество цифр в числе.
Входные данные: N целое число.
Выходные данные: kol количество цифр в числе.
Промежуточные данные: M переменная для временного хранения значения N.
Для того чтобы подсчитать количество цифр в числе, необходимо определить, сколько раз заданное число можно разделить на десять нацело. Например, пусть N=12345, тогда количество цифр kol = 5. Результаты вычислений сведены в таблицу 3.8. Алгоритм определения количества цифр в числе представлен на рис. 3.35.
Таблица 3.8. Определение количества цифр числа Текст программы, реализующей данную задачу, можно записать так:
var M,N: longint ;
begin {Так как речь идёт о натуральных числах,} {при вводе предусмотрена проверка.} {Закончить цикл, если введено положительное число,} {иначе повторить ввод} repeat M:=N;
kol :=1;
while M div 10 > 0 do {Выполнять тело цикла, пока число делится нацело на 10.} begin Рис. 3.35. Алгоритм определения количества цифр в числе end.
ЗАДАЧА 3.17. Дано натуральное число N. Определить, содержит ли это число нули и в каких разрядах они расположены (например, число 1 101 111 011 содержит ноль в третьем и восьмом разрядах).
Входные данные: N целое число.
Выходные данные: pos позиция цифры в числе.
Промежуточные данные: i параметр цикла, M переменная для временного хранения значения N.
В связи с тем, что разряды в числе выделяются, начиная с последнего, то для определения номера разряда в числе, необходимо знать количество цифр в числе12. Таким образом, на первом этапе решения задачи необходимо определить kol количество цифр в числе. Затем начинаем выделять из числа цифры;
если очередная цифра равна нулю, нужно вывести на экран номер разряда, который занимает эта цифра. Процесс определения текущей цифры числа N= представлен в таблице 3.9.
Блок-схема алгоритма решения данной задачи показана на рис. 3.36.
12 Алгоритм нахождения количества цифр в числе был рассмотрен в предыдущей задаче.
Таблица 3.9. Определение текущей цифры числа Текст программы, реализующей данный алгоритм:
var M,N: longint ;
begin {Так как речь идет о натуральных числах,} {при вводе предусмотрена проверка.} {Закончить цикл, если введено положительное число,} {иначе повторить ввод} repeat //Определение kol - количества разрядов.
M:=N;
kol :=1;
while M div 10 > 0 do {Выполнять тело цикла, пока число делится нацело на 10.} begin M:=N;
begin {Выделение цифры из числа и сравнение её с нулем.} i f pos=0 then writeln ( ’ Число не содержит цифру 0. ’ ) ;
end.
3.5.6 Ввод данных из диалогового окна в среде Lazarus Окно ввода это стандартное диалоговое окно, которое появляется на экране в результате вызова функции InputBox. В общем виде оператор ввода данных с использованием этой функции записывают так:
имя:= InputBox ( заголовок_окна, подсказка, значение ) ;
где • заголовок_окна строка, определяющая название окна;
• подсказка текст поясняющего сообщения;
• значение строка, которая будет находиться в поле ввода при появлении • имя переменная строкового типа, которой будет присвоено значение из После выполнения фрагмента программы var S : string ;
begin S:= InputBox ( ’ ЗАГОЛОВОК ОКНА ’, ’ Подсказка : введите исходные данные ’, ’ Данное значение ’ ) ;
end ;
появится окно, представленное на рис. 3.37.
У пользователя есть возможность изменять текст в поле ввода. Щелчок по кнопке ОК приведёт к тому, что в переменную, указанную слева от оператора присваивания, будет занесена строка, находящаяся в поле ввода. В данном случае в переменную S будет записана строка ’Данное значение’. Щелчок по кнопке Cancel закроет окно ввода.
Учитывая, что функция InputBox возвращает строковое значение, при вводе числовых данных применяют функции преобразования типов:
begin S:= InputBox ( ’ Ввод данных ’, MessageDlg ( ’ Величина угла в градусах ’ end ;
Можно применять диалоговое окно при решении задач, обрабатывающих некоторые числовые последовательности. Рассмотрим несколько таких задач.
ЗАДАЧА 3.18. Поступает последовательность из N вещественных чисел. Определить наибольший элемент последовательности.
Входные данные: N целое число; Х вещественное число, определяет текущий элемент последовательности.
Выходные данные: Max вещественное число, элемент последовательности с наибольшим значением.
Промежуточные переменные: i параметр цикла, номер вводимого элемента последовательности.
Алгоритм поиска наибольшего элемента в последовательности следующий (рис. 3.38).
В памяти компьютера отводится ячейка, например, с именем Max, в которой будет храниться наибольший элемент последовательности максимум. Вводим количество элементов последовательности и первый элемент последовательности. Предполагаем, что первый элемент последовательности наибольший, и записываем его в Max. Затем вводится второй элемент последовательности и сравнивается с предполагаемым максимумом. Если окажется, что второй элемент больше, его записываем в ячейку Max. В противном случае никаких действий не предпринимаем. Потом переходим к вводу следующего элемента последовательности, и алгоритм повторяется с начала. В результате, в ячейке Max будет храниться элемент последовательности с наибольшим значением13.
Разместим на форме объект типа надпись Label1 и кнопку Button1 (см. рисунок 3.39). Щелчок по кнопке приведёт к выполнению следующей процедуры:
13 Для поиска наименьшего элемента последовательности (минимума), предполагают, что первый элемент наименьший, записывают его в ячейку Min, а затем среди элементов последовательности ищут число, значение которого будет меньше, чем предполагаемый минимум Рис. 3.38. Алгоритм поиска наибольшего числа в последовательности procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
var i,N: integer ; max,X: r e a l ; S : string ; begin //Ввод количества элементов последовательности.
S:= InputBox ( ’ Ввод ’, ’ Введите количество элементов в последовательности. ’, ’ 0 ’ ) ;
//Ввод первого элемента последовательности.
S:= InputBox ( ’ Ввод элементов последовательности ’, //Предположим, что первый элемент максимальный Max=X.
max:=X;
//Параметр цикла принимает стартовое значение i=2, //т.к. первый элемент уже введён.
begin //Ввод следующих элементов последовательности.
S:= InputBox ( ’ Ввод элементов последовательности ’, //Если найдётся элемент, превышающий максимум, //записать его в ячейку Max //теперь он предполагаемый максимум.
i f X>max then max:=X;
//Вывод наибольшего элемента последовательности.
MessageDlg ( ’ Значение наибольшего элемента ’ end ;
Результаты работы программы представлены на рис. 3.39 3.42.
ЗАДАЧА 3.19. Вводится последовательность целых чисел, 0 конец последовательности. Найти наименьшее число среди положительных, если таких значений несколько14, определить, сколько их.
Блок-схема решения задачи приведена на рис. 3.43.
Рис. 3.43. Алгоритм поиска минимального положительного числа в последовательности Далее приведён текст подпрограммы с подробными комментариями15. Подпрограмма выполняется при обращении к кнопке Button1, предварительно размещённой на форме.
procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
var N, k, min : integer ; S : string ;
begin //Ввод первого элемента последовательности.
S:= InputBox ( ’ Ввод элементов последовательности ’, ’ введите число. 0 конец последовательности ’, ’ 0 ’ ) ;
//Предположим, что положительных чисел нет K=0. В переменной //K будет храниться количество минимумов среди положительных //чисел.
k :=0;
//Пока введённое число не равно нулю, выполнять тело цикла.
while N0 do begin //Проверяем, является ли введённое число положительным.
//Если N>0 и K=0, поступил 1-й положительный элемент, //предположим, что он минимальный Min=N, соответственно, //количество минимумов равно 1.
//Если элемент не первый, сравниваем его //с предполагаемым минимумом;
//если элемент меньше, записываем его //в Min и обнуляем счетчик.
e l s e i f N0 then //Если элемент положительный, то max_min(X, X_max, X_min ) ;
//Печать результатов.
writeln ( ’ max= ’,X_max, ’ min= ’,X_min ) ;
end.
4.4 Функции Описание функции также состоит из заголовка и тела:
function имя_функции(список_формальных_параметров ) :
label список_меток ;
const список_констант ;
type список_типов ;
список_переменных ;
begin //Тело функции.
Заголовок функции содержит: служебное слово function, любой допустимый в языке Free Pascal идентификатор имя_функции; имена формальных параметров и их типы, разделённые точкой с запятой список_формальных_параметров, тип возвращаемого функцией значения тип_результата (функции могут возвращать скалярные значения целого, вещественного, логического, символьного или ссылочного типов).
Примеры описания функций:
function fun_1 ( x : r e a l ) : r e a l ;
function fun_2 ( a, b : integer ) : r e a l ;
Тело функции состоит из раздела описаний3 (константы, типы, переменные, процедуры и функции, используемые в процедуре) и операторов языка, реализующих её алгоритм. В теле функции всегда должен быть хотя бы один оператор, присваивающий значение имени функции.
Например:
function fun_2 ( a, b : integer ) : r e a l ;
begin 3 Раздел описаний в функции может отсутствовать, если в нём нет необходимости.
Обращение к функции осуществляется по имени с указанием списка фактических параметров:
имя_функции ( список_фактических_параметров ) ;
Например:
y:= fun_1 ( 1. 2 8 ) ;
ЗАДАЧА 4.3. Вводится последовательность из N целых чисел, найти среднее арифметическое совершенных чисел и среднее геометрическое простых чисел последовательности.
Напомним, что целое число называется простым, если оно делится нацело только на само себя и единицу. Подробно алгоритм определения простого числа описан в задаче 3.14 (рис. 3.33). Кроме простых чисел, в этой задаче фигурируют совершенные числа. Число называется совершенным, если сумма всех делителей, меньших его самого, равна этому числу. Алгоритм, с помощью которого можно определить делители числа, подробно рассмотрен в задаче 3.13 (рис. 3.32).
Для решения поставленной задачи понадобятся две функции:
• prostoe определяет, является ли число простым, аргумент функции целое число N, функция возвращает true (истина), если число простое, и false (ложь) в противном случае;
• soversh определяет, является ли число совершенным; входной параметр целое число N, функция возвращает true (истина), если число простое, и false (ложь) в противном случае.
Фрагмент программы с комментариями:
//Функция, которая определяет простое число.
function p r o s t o e (N: word ) : boolean ;
var i : word ;
begin //Функция, которая определяет совершенное число.
function s o v e r s h (N: word ) : boolean ;
var i : word ; S : word ;
begin var X: word ; K, kol_p, kol_s, i : byte ; Sum, Pro : r e a l ;
//Начало основной программы.
begin //Ввод количества элементов в последовательности.
write ( ’K= ’ ) ; readln (K) ;
begin //Ввод элемента последовательности.
//выполнить операцию умножения, //увеличить счётчик простых чисел.
//выполнить операцию умножения, //увеличить счётчик совершенных чисел.
//Если были найдены совершенные числа, i f kol_s 0 then begin Sum:=Sum/ kol_s ; //вычислить среднее арифметическое.
writeln ( ’ Среднее арифметическое совершенных чисел ’, else writeln ( ’ Совершенных чисел в последовательности нет. ’ ) ;
i f kol_p0 then begin //вычислить среднее геометрическое.
writeln ( ’ Среднее геометрическое простых чисел ’, Pro : 5 : 2 ) ;
e l s e //иначе вывести сообщение:
writeln ( ’ Простых чисел в последовательности нет ’ ) ;
end.
ЗАДАЧА 4.4. Вводится последовательность целых чисел. 0 конец последовательности. Определить, содержит ли последовательность хотя бы одно число-палиндром.
Палиндром это число, симметричное относительно своей середины. Например, 123 454 321, 678 876 палиндромы. Чтобы определить, является ли число палиндромом, нужно сравнивать первую и последнюю цифры, затем вторую и предпоследнюю и так далее. Если хотя бы в одной паре цифры не совпадут, то число палиндромом не является.
Для решения поставленной задачи понадобятся две функции:
• cifra_kol определяет количество цифр в числе (подробно алгоритм описан в задаче 3.16);
• palindrom возвращает значение истина, если переданное в неё число палиндромом.
Текст программы с комментариями:
//Функция для вычисления количества цифр в числе M.
function c i f r a _ k o l (M: longint ) : byte ;
begin cifra_kol :=1;
//Функция возвращает значение истина, если число M, //состоящее из kol цифр, палиндром, //и значение ложь, в противном случае.
function palindrom (M: longint ; k o l : byte ) : boolean ;
var i : byte ; j : longint ;
begin j :=1;
//Возведение числа 10 в степень kol-1 (разрядность числа).
palindrom := true ; //Пусть число - палиндром.
begin //Выделение старшего разряда M div j (первая цифра).
//Выделение младшего разряда M mod 10 (последняя цифра).
//Если первая и последняя цифры не совпадают, //Изменение числа end ;
//Основная программа.
var X: longint ; pr : boolean ;
begin write ( ’X= ’ ) ; readln (X ) ; //Ввод элемента последовательности.
while X0 do //Пока не ноль, begin //Ввод следующего элемента последовательности.
i f pr then writeln ( ’ Последовательность ’, e l s e writeln ( ’ Последовательность не содержит палиндромов. ’ ) ;
end.
4.5 Решение задач с использованием подпрограмм В этом разделе мы рассмотрим задачи с несложными алгоритмами, но больше внимания уделим их интерфейсу в среде Lazarus.
ЗАДАЧА 4.5. Создать программу, которая автоматизирует процесс перевода градусной меры угла в радианную и наоборот, в зависимости от выбора пользователя. То есть пользователь должен выбрать, как он будет вводить угол: в радианах или в градусах. Если введёт в радианах, ответ получит в градусах и, соответственно, введёт в градусах ответ получит в радианах.
С точки зрения математика задача не вызывает сложности:
• чтобы найти радианную меру какого-нибудь угла по данной градусной мере, нужно помножить число градусов на /180, число минут на /(180 · 60), число секунд на /(180 · 60 · 60) и найденные произведения сложить;
• чтобы найти градусную меру угла по заданной радианной, нужно помножить число радиан на 180/; если из полученной дроби выделить целую часть, то получим градусы; если из числа, полученного умножением оставшейся дробной части 60, выделить целую часть, получим минуты; секунды вычисляются аналогично из дробной части минут.
Для перевода угла из градусной меры в радианную создадим функцию function gradus_radian ( gradus, minuta, secunda : byte ) : r e a l ;
в которую будем передавать целочисленные значения градусов, минут и секунд.
Результат работы функции вещественное число, величина угла в радианах.
Задачу перевода из радианной меры в градусную решим, создав процедуру procedure radian_gradus ( r a d i a n : r e a l ;
у которой один входной параметр радианная мера угла и три выходных градусы, минуты и секунды.
Разработаем интерфейс будущей программы в среде Lazarus. Создадим новый проект, установим свойства формы так, как показано в табл. 4.1, и разместим на ней компоненты в соответствии с рис. 4.1.
С компонентами Edit, Label и Button мы уже хорошо знакомы. Компонент RadioButton это переключатель. Его используют для выбора одного из нескольких взаимоисключающих решений. Обычно на форму помещается, по меньшей мере, два таких компонента. Они могут иметь только два состояния, определяемых свойством Checked. Если у одного из компонентов это свойство истинно (true), то во всех остальных ложно (false). В данной задаче используется два компонента: RadioButton1 и RadioButton2, предоставляя пользователю выбор: включён первый компонент будет осуществлён перевод из градусной BorderIcons.BiMaximize false меры в радианную, включен второй наоборот. Двойной щелчок по компоненту RadioButton1 приведёт к созданию процедуры TForm1.RadioButton1Click обработки события щелчок мыши по кнопке переключателю. В тексте процедуры следует указать команды, которые будут выполняться, если пользователь включил или выключил компонент.
Нам уже известно, что свойства компонентов могут изменяться как в окне конструирования формы, так и непосредственно в программе. Если дважды щелкнуть по форме, вне размещённых на ней компонентов, то будет создана процедура TForm1.FormCreate обработки события открытия формы. На вкладке События инспектора объектов это событие носит название OnCreate. Процедура OnCreate выполняется в момент создания формы. Все действия, включённые в неё, будут выполнены перед первым выводом формы на экран. Здесь можно задать свойства всех компонентов на момент открытия формы.
Кнопки, расположенные на форме, несут следующую функциональную нагрузку:
• Button1 запускает процесс перевода в зависимости от установок переключателей;
• Button3 возвращает внешний вид формы в первоначальное состояние (до ввода и вывода данных);
• Button2 завершает процесс выполнения программы.
Текст программы с необходимыми комментариями приведён ниже.
unit Unit1 ;
interface uses type TForm1 = c l a s s (TForm) Button1 : TButton ;
Label1 : TLabel ;
RadioButton1 : TRadioButton ;
RadioButton2 : TRadioButton ;
Edit1 : TEdit ;
Button2 : TButton ;
Label2 : TLabel ;
Edit2 : TEdit ;
Edit3 : TEdit ;
Label3 : TLabel ;
Label4 : TLabel ;
Label5 : TLabel ;
Edit4 : TEdit ;
Button3 : TButton ;
procedure B u t t o n 1 C l i c k ( Sender : TObject ) ;
procedure RadioButton1Click ( Sender : TObject ) ;
procedure RadioButton2Click ( Sender : TObject ) ;
procedure FormCreate ( Sender : TObject ) ;
procedure B u t t o n 2 C l i c k ( Sender : TObject ) ;
procedure B u t t o n 3 C l i c k ( Sender : TObject ) ;
private { Private declarations } public { Public declarations } end ;
var Form1 : TForm1 ;
implementation {$R *.dfm} //Щелчок по кнопке ВЫЧИСЛИТЬ.
procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
//Функция перевода данных из градусов в радианы.
function gradus_radian ( gradus, minuta, secunda : byte ) : r e a l ;
begin gradus_radian := //Процедура перевода из радиан в градусы.
procedure radian_gradus ( r a d i a n : r e a l ;
begin var grad, min, s e c : byte ;
kod_g, kod_m, kod_s, kod_r : integer ; //Контроль ввода.
begin i f RadioButton1. Checked then begin Val ( Edit1. Text, grad, kod_g ) ;
Val ( Edit2. Text, min, kod_m ) ;
Val ( Edit3. Text, s e c, kod_s ) ;
//Если ошибки при вводе не было, то i f ( kod_g=0) and (kod_m=0) and ( kod_s=0) then begin //Вызов функции gradus_radian перевода из градусов в радианы.
Label2. Caption := ’ Величина угла ’ +chr ( 1 3 ) +FloatToStrF ( gradus_radian ( grad, min, s e c ), f f F i x e d, 8, 6 ) MessageDlg ( ’ Ошибка при вводе данных ! ’, MtWarning, [ mbOk ], 0 ) ;
end ;
i f RadioButton2. Checked then begin Val ( Edit4. Text, rad, kod_r ) ;
i f ( kod_r=0)then begin //и вывести туда результат вычислений.
//Вызов процедуры перевода из радиан в градусы.
radian_gradus ( rad, grad, min, s e c ) ;
Label2. Caption := ’ Величина угла ’ +chr ( 1 3 ) //Иначе выдать сообщение об ошибке при вводе.
else MessageDlg ( ’ Ошибка при вводе данных ! ’, MtWarning, [ mbOk ], 0 ) ;
end ;
end ;
//Щелчок по кнопке ВЫХОД.
procedure TForm1. B u t t o n 2 C l i c k ( Sender : TObject ) ;
begin close ;
end ;
//Щелчок по кнопке ОЧИСТИТЬ.
procedure TForm1. B u t t o n 3 C l i c k ( Sender : TObject ) ;
begin //Установка свойств компонентов в первоначальное состояние.
Edit1. Text := ’ 00 ’ ;
Edit2. Text := ’ 00 ’ ;
Edit3. Text := ’ 00 ’ ;
Label1. Caption := ’ Введите значение ’ ;
Label3. Caption := ’ Градусы ’ ;
Label4. Caption := ’ Минуты ’ ;
Label5. Caption := ’ Секунды ’ ;
Button1. Caption := ’ ВЫЧИСЛИТЬ ’ ;
Button2. Caption := ’ ВЫХОД ’ ;
Button3. Caption := ’ ОЧИСТИТЬ ’ ;
Edit4. Enabled := f a l s e ;
RadioButton1. Checked := true ;
RadioButton2. Checked := f a l s e ;
end ;
//Обработка события открытие формы.
procedure TForm1. FormCreate ( Sender : TObject ) ;
begin //Установка свойств компонентов.
Edit1. Text := ’ 00 ’ ;
Edit2. Text := ’ 00 ’ ;
Edit3. Text := ’ 00 ’ ;
Label1. Caption := ’ Введите значение ’ ;
Label3. Caption := ’ Градусы ’ ;
Label4. Caption := ’ Минуты ’ ;
Label5. Caption := ’ Секунды ’ ;
Button1. Caption := ’ ВЫЧИСЛИТЬ ’ ;
Button2. Caption := ’ ВЫХОД ’ ;
Button3. Caption := ’ ОЧИСТИТЬ ’ ;
Edit4. Enabled := f a l s e ;
RadioButton1. Checked := true ;
RadioButton2. Checked := f a l s e ;
end ;
//Обработка события щелчок по переключателю RadioButton1.
procedure TForm1. RadioButton1Click ( Sender : TObject ) ;
begin i f RadioButton1. Checked then begin Edit1. Enabled := true ;
Edit2. Enabled := true ;
Edit3. Enabled := true ;
Label5. Enabled := true ;
Label3. Enabled := true ;
Label4. Enabled := true ;
end ;
//Обработка события щелчок по переключателю RadioButton2.
procedure TForm1. RadioButton2Click ( Sender : TObject ) ;
begin i f RadioButton2. Checked then begin Edit4. Enabled := true ;
Button1. Enabled := true ;
end ; end.
Результаты работы программы представлены на рис. 4.2 и рис. 4.3.
Рис. 4.2. Перевод значений из градус- Рис. 4.3. Перевод значений из радианной меры в радианную ной меры в градусную ЗАДАЧА 4.6. Создать программу для решения уравнений:
• кубическое ax3 + bx2 + cx + d.
Решение линейного уравнения тривиально: x = b/a; алгоритмы решения квадратного и кубического уравнений подробно рассмотрены в задачах 3.4 и 3.5.
Создадим новый проект. Свойства формы настроим по табл. 4.1, за исключением свойства Caption, которому присвоим значение Решение уравнения. Компоненты на форме разместим так, как показано на рис. 4.4.
Обратите внимание, что на форме появились не знакомые нам компоненты.
Это CheckBox флажок и RadioGroup группа переключателей.
Компонент флажок CheckBox используется для того, чтобы пользователь могут указать свое решение: да или нет. Установлен флажок или нет, определяет свойство Checked (true, false). В составе диалогового окна может быть несколько таких компонентов, причём состояние любого из них не зависит от состояния остальных.
Компонент группа переключателей RadioGroup объединяет в себе несколько переключателей. Каждый размещённый в нём переключатель помещается в специальный список Items и доступен по номеру, установленному в свойстве ItemIndex. После размещения на форме компонент пуст. Чтобы создать в нём хотя бы один переключатель, нужно выделить его, обратиться к инспектору объектов и выбрать свойство Items редактор списка. Строки, набранные в редакторе, используются как поясняющие надписи справа от переключателей, а их количество определяет количество переключателей в группе. В нашем случае окно редактора списка будет иметь вид, как на рис. 4.5.
После создания компонента группа переключателей, его свойство номер переключателя ItemIndex по умолчанию равно 1. Это означает, что ни один компонент в группе не установлен. Чтобы в момент появления компонента на экране один из переключателей был отмечен, нужно либо на этапе конструирования формы, либо программно присвоить свойству ItemIndex номер одного из элементов списка, учитывая, что нумерация начинается с нуля.
С остальными компонентами, размещёнными на форме, пользователь уже знаком. На рис. 4.6 4.8 видно, как работает программа. Пользователю предоставляется возможность выбрать вид решаемого уравнения, ввести его коэффициенты и указать, какие решения действительные или комплексные (если это возможно) он хотел бы получить.
4.5. Решение задач с использованием подпрограмм Далее приведён текст программного модуля с комментариями:
unit Unit1 ;
interface uses type TForm1 = c l a s s (TForm) Label1 : TLabel ;
Label2 : TLabel ;
Label3 : TLabel ;
Label4 : TLabel ;
Label5 : TLabel ;
Edit1 : TEdit ;
Edit2 : TEdit ;
Edit3 : TEdit ;
Edit4 : TEdit ;
CheckBox1 : TCheckBox ;
CheckBox2 : TCheckBox ;
Button1 : TButton ;
Button2 : TButton ;
RadioGroup1 : TRadioGroup ;
Button3 : TButton ;
procedure FormCreate ( Sender : TObject ) ;
procedure B u t t o n 2 C l i c k ( Sender : TObject ) ;
procedure B u t t o n 3 C l i c k ( Sender : TObject ) ;
procedure B u t t o n 1 C l i c k ( Sender : TObject ) ;
procedure RadioGroup1Click ( Sender : TObject ) ;
private { Private declarations } public { Public declarations } end ;
var Form1 : TForm1 ;
implementation {$R *.dfm} //Щелчок по кнопке НАЙТИ КОРНИ.
procedure TForm1. B u t t o n 1 C l i c k ( Sender : TObject ) ;
//Решение линейного уравнения.
procedure korni_1 ( a, b : r e a l ; var x_ : string ) ;
begin //Решение квадратного уравнения.
procedure korni_2 ( a, b, c : r e a l ; var x1_, x2_ : string ;
begin i f d1), то количество серий (kol) опять увеличиваем на 1. В завершении выводим количество серий из знакочередующихся элементов переменную kol. Блок-схема решения задачи 5.8 представлена на рис. 5.40.
Ниже приведён текст консольного приложения на языке Free Pascal.
begin write ( ’ n= ’ ) ; readln ( n ) ;
f or i :=1 to n do {Так как минимальная серия состоит из двух элементов,} {k присвоим значение 1.} k : = 1 ; {Длина серии.} k o l : = 0 ; {Количество серий в массиве.} f or i :=1 to n1 do {Если при умножении двух соседних элементов результат - отрицательное} {число, то элементы имеют разный знак.} {Подготовить показатель продолжения серии} {к возможному появлению следующей серии.} {Проверка, не было ли серии в конце массива.} i f k>1 then {Если да, увеличить счетчик еще на единицу.} i f kol >0 then write ( ’ Количество знакочередующихся серий= ’, k o l ) e l s e write ( ’ Знакочередующихся серий нет ’ ) end.
Далее рассмотрим чуть более сложную задачу на серии.
232 Глава 5. Использование языка Free Pascal для обработки массивов Рис. 5.40. Блок-схема решения задачи 5. ЗАДАЧА 5.9. В заданном массиве найти самую длинную серию элементов, состоящую из единиц.
Для максимальной серии будем хранить её длину (max) и номер последнего элемента (kon_max).
Эта задача похожа на предыдущую, отличие заключается в том, что надо фиксировать не только тот факт, что серия кончилась, но и саму серию. Серия может характеризоваться двумя из трёх параметров: первый элемент серии, последний элемент серии, длина серии. В связи с тем, что мы фиксируем серию в момент её окончания, то в качестве параметров серии будем использовать последний элемент серии (kon) и её длину (k).
Алгоритм решения этой задачи следующий. Вначале количество серий (kol) и её длина (k) равны нулю. Перебираем последовательно все элементы, если текущий элемент равен 1, то количество элементов в серии11 увеличиваем на 1.
Если текущий элемент не равен 1, то возможны два варианта: либо сейчас оборвалась серия из единиц, либо встретился очередной неравный единице элемент.
Выбор из этих двух вариантов можно осуществить, сравнив переменную k с единицей. Если k>1, сейчас оборвалась серия из единиц, и количество таких серий (kol) надо увеличить на 1, зафиксировать конец серии (kon:=i-1) и длину серии (dlina:=k). После этого необходимо проверить порядковый номер серии. Если это первая серия (kol=1), то объявляем ее максимальной, в переменную max записываем длину текущей серии k, в переменную kon_max kon (последний элемент текущей серии). Если это не первая серия (kol>1), то длину текущей серии (k) сравниваем с длиной серии максимальной длины (max). И если k>max, то текущую серию объявляем серией максимальной длины (max:=k; kon_max:=kon;).
Если встретился не равный нулю элемент, надо количество элементов в серии положить равным нулю (k:=0).
После выхода из цикла надо также проверить, не было ли в конце серии, состоящей из единиц. Если серия была в конце, то следует обработать её так же, как и серию, которая встретилась в цикле.
Блок-схема решения задачи приведена на рис. 5.41.
Ниже приведён текст консольного приложения решения задачи.
var x : array [ 1.. 5 0 ] of integer ;
n, i, k, kol, kon, max, kon_max, d l i n a : integer ;
begin {Ввод размера массива.} write ( ’ n= ’ ) ;
readln ( n ) ;
{Ввод массива} writeln ( ’ Массив Х ’ ) ;
11 Количество подряд идущих единиц.
234 Глава 5. Использование языка Free Pascal для обработки массивов Рис. 5.41. Блок-схема решения задачи 5. f or i :=1 to n do {Начальное присваивание длины серии и количества серий} k : = 0 ; {Длина серии.} k o l : = 0 ; {Количество серий в массиве.} {Перебираем все элементы в массиве} f or i :=1 to n do {Если текущий элемент равен 1, то} i f x [ i ]=1 then {количество подряд идущих единиц увеличить на 1.} k:=k+ else {Если текущий элемент не равен 1, то} begin {проверяем, была ли серия до этого, k>1?} {Если только что оборвалась серия, то} {увеличиваем количество серий.} {Фиксируем тот факт, что на предыдущем элементе серия закончилась,} {длина серии равна k.} {Если это первая серия,} {объявляем ее максимальной.} {Длина максимальной серии единиц.} {Конец максимальной серии, состоящей из единиц, хранится в переменной} {kon_max.} {Если это не первая серия, состоящая из единиц,} {то её длину сравниваем с длиной серии с максимальным количеством} {единиц.} {Если длина текущей серии больше,} {то объявляем ее максимальной.} 236 Глава 5. Использование языка Free Pascal для обработки массивов {Если текущий элемент массива не равен 0, то количество подряд} {встречающихся единиц начинаем считать сначала (k:=0).} end ;
{Проверка, не было ли серии в конце массива.} i f k>1 then {Если да, увеличить счётчик ещё на единицу.} begin {Серия закончилась на последнем элементе.} {Обработка последней серии так, как это происходило в цикле.} end ;
{Если серии были, то} {вывод информации о серии с максимальным количеством единиц.} begin writeln ( ’ Количество серий, состоящих из единиц= ’, k o l ) ;
writeln ( ’ Наибольшая серия начинается с номера ’, kon_maxmax+1, ’, заканчивается номером ’, kon_max, {Вывод информации об отсутствии серий.} else writeln ( ’ Нет серий, состоящих из единиц ’ ) end.
ЗАДАЧА 5.10. Задан массив вещественных чисел. Перевести все элементы массива в p-ричную систему счисления.
Перед решением всей задачи давайте разберёмся с алгоритмом перевода вещественного числа из десятичной в другую систему счисления. Этот алгоритм можно разделить на следующие этапы:
1) Выделение целой и дробной частей числа.
2) Перевод целой части числа в другую систему счисления.
3) Перевод дробной части числа в другую систему счисления.
4) Объединение целой и дробной частей числа в новой системе счисления.
Алгоритм перевода целого числа в другую систему счисления Разделить нацело число на основание новой системы счисления. Получим остаток и частное. Остаток от деления будет младшим разрядом числа. Его необходимо будет умножить на 10 в нулевой степени. Если частное не равно нулю, то продолжим деление; новый остаток даст нам следующий разряд числа, который надо будет умножить на десять в первой степени и т. д. Деление будем продолжать до тех пор, пока частное не станет равным 0. Особенностью алгоритма является то, что число формируется в обратном порядке от младшего разряда к старшему, что позволит в один проход собрать число в новой системе счисления.
Алгоритм перевода дробной части числа в другую систему счисления Умножить дробную часть числа на основание системы счисления. В полученном произведении выделить целую часть числа, это будет старший разряд числа, который необходимо будет умножить на 101. Дробную часть опять умножить на основание системы счисления. В произведении целая часть будет очередным разрядом (его надо будет умножить на 102 ), а дробную часть необходимо опять умножить на основание системы счисления до получения необходимого количества разрядов исходного числа.
Блок-схема функции перевода вещественного числа N из десятичной системы счисления в другую систему представлена на рис. 5.42.
Обратите внимание, как в блок-схеме и в функции реализовано возведение в степень. В связи с тем, что при переводе целой части числа последовательно используются степени 10, начиная с 0, для формирования степеней десяти вводится переменная q, которая вначале равна 1, а затем в цикле последовательно умножается на 10. При переводе дробной части числа последовательно нужны отрицательные степени 10 : 101,102,.... Поэтому при формировании дробной части числа переменная q:=0.1, которая в цикле последовательно делится на 10.
Ниже приведён текст консольной программы решения задачи 5.10 с комментариями.
238 Глава 5. Использование языка Free Pascal для обработки массивов Рис. 5.42. Блок-схема функции перевода вещественного числа в p-ричную систему счисления {Функция перевода вещественного числа в p-ричную систему счисления.} {Входные параметры функции: вещественное число N, основание системы} {счисления - целое число p, kvo - количество разрядов в дробной части} {формируемого числа.} function perevod (N: r e a l ; P : word ; kvo : word ) : r e a l ;
var i,N1, o s t : word ;
begin {Если исходное число отрицательно, то для его перевода рекурсивно} {обращаемся к функции perevod, передавая в качестве параметра модуль} {числа.} i f N1 then {И текущий разряд меньше или равен предыдущему, цифры числа N в} {восьмеричном представлении не образуют убывающую последовательность} {(pr:=false) - аварийно покидаем цикл.} {и текущий разряд меньше или равен предыдущему, цифры числа N в} {восьмеричном представлении не образуют убывающую последовательность} {(pr:=false) - аварийно покидаем цикл.} 6.2 Алгоритмы и программы работы с матрицами Рассмотри несколько примеров решения задач обработки матриц.
ЗАДАЧА 6.3. Найти сумму элементов матрицы, лежащих выше главной диагонали (см. рис. 6.13).
Рассмотрим два алгоритма решения данной задачи.
Первый алгоритм решения данной задачи (см. рис. 6.14) построен следующим образом. Вначале переменная S для накапливания суммы обнуляется (S:=0).
Затем с помощью двух циклов (первый по строкам, второй по столбцам) перебираются все элементы матрицы, но накапливание суммы происходит только в том случае, если этот элемент находится выше главной диагонали (если выполняется свойство ii then {Если элемент лежит выше главной диагонали, то} s := s+a [ i, j ] ; {наращиваем сумму.} writeln ( ’ матрица А ’ ) ;
f or i :=1 to n do begin f or j :=1 to m do {Здесь важен формат, особенно общая ширина поля!} writeln end ;
writeln ( ’ сумма элементов матрицы ’, s : 8 : 3 ) ;
end.
Результаты работы программы представлены на рис. 6.16.
Второй алгоритм решения этой задачи представлен на рис. 6.15.
В нём проверка условия i0) then k:=k+1;
Так как угловые элементы матрицы уже учтены при проверке диагональных элементов, при обработке элементов, расположенных по периметру матрицы, их учитывать уже не нужно. Поэтому надо перебрать элементы со второго до предпоследнего в первой и последней строке, в первом и последнем столбце.
f or i :=2 to N1 do begin {Если элемент находится в первой строке.} {Если элемент находится в последней строке.} {Если элемент находится в первом столбце.} {Если элемент находится в последнем столбце.} end ;
Затем нужно проверить, не был ли элемент, находящийся на пересечении диагоналей, подсчитан дважды. Это могло произойти только в том случае, если N нечётно и элемент, расположенный на пересечении диагоналей4, положителен.
k:=k 1;
Ниже приведён полный текст консольного приложения решения задачи 6. с комментариями.
var begin write ( ’N= ’ ) ;
readln (N ) ;
//Ввод исходной матрицы.
writeln ( ’ Введите матрицу A ’ ) ;
//Вывод исходной матрицы.
writeln ( ’ Была введена матрица A: ’ ) ;
begin k :=0;
//Обработка элементов, расположенных на диагоналях матрицы.
begin end ;
//Обработка элементов, расположенных по периметру матрицы.
begin end ;
4 На пересечении диагоналей находится элемент с индексами (N div 2 + 1, N div 2 + 1).
{Если элемент, находящийся на пересечении диагоналей, подсчитан дважды,} {то уменьшить вычисленное значение к на один.} i f ( n mod 20) and ( a [N div 2+1,N div 2+1] >0) then k:=k 1;
end.
На рис. 6.18 представлены результаты работы программы решения задачи 6.4.
Введите матрицу А 11 22 33 44 - -6 77 33 - 22 33 44 17 -8 -9 64 76 -7 Была введена матрица А:
11 22 33 44 - -6 77 33 - 22 33 44 17 -8 -9 64 76 -7 k= ЗАДАЧА 6.5. Проверить, является ли заданная квадратная матрица единичной.
Единичной называют матрицу, у которой элементы главной диагонали единицы, а все остальные нули. Например, Решать задачу будем так. Предположим, что матрица единичная (pr:=true) и попытаемся доказать обратное. В двойном цикле по по строкам (i:=1,2,...,N) и по по столбцам (j:=1,2,...,N) перебираем все элементы матрицы. Если диагональный элемент (i = j) не равен единице или элемент, расположенный вне Рис. 6.19. Блок-схема алгоритма решения задачи 6. диагонали (i = j), не равен нулю5, то в логическую переменную pr записываем значение false и прекращаем проверку (аварийно покидаем цикл). После цикла проверяем значение pr. Если переменная pr по прежнему равна true, то матрица единичная; в противном случае она таковой не является. Блок-схема алгоритма решения задачи представлена на рис. 6.19.
program pr_6_5 ;
pr : boolean ;
begin writeln ( ’ Введите размер матрицы ’ ) ;
writeln ( ’ Введите матрицу ’ ) ;
{Предположим, что матрица единичная,} {и присвоим логической переменной значение "истина".} {Если значение этой переменной при выходе из цикла не изменится, это} {будет означать, что матрица действительно единичная.} 5 Воспользовавшись логическими операциями and и or это сложное условие можно записать так: if ((i=j) and (a[i,j]1)) or ((ij) and (a[i,j]0)) then...
{Если элемент лежит на главной диагонали и не равен единице или элемент} {лежит вне главной диагонали и не равен нулю, то} begin {логической переменной присвоить значение "ложь"} {это будет означать, что матрица единичной не является,} {выйти из цикла.} break ;
end ;
{Проверка значения логической переменной и печать результата.} i f pr then writeln ( ’ Матрица единичная ’ ) e l s e writeln ( ’ Матрица не является единичной ’ ) ;
end.
ЗАДАЧА 6.6. Преобразовать исходную матрицу так, чтобы последний элемент каждого столбца был заменён разностью минимального и максимального элемента в этом же столбце.
Для решения данной задачи необходимо найти в каждом столбце максимальный и минимальный элементы, после чего в последний элемент столбца записать их разность. Блок-схема алгоритма решения приведена на рис. 6.20.
Ниже приведён текст консольного приложения с комментариями.
program pr_6_6 ;
i, j, n,m: integer ;
begin {Ввод размеров матрицы.} writeln ( ’ Введите размеры матрицы ’ ) ;
readln ( n,m) ;
{Ввод исходной матрицы.} writeln ( ’ Введите матрицу ’ ) ;
{Последовательно перебираем все столбцы матрицы.} Рис. 6.20. Блок-схема алгоритма решения задачи 6. begin {Максимальным и минимальным объявляем первый элемент текущего (j-го)} {столбца матрицы.} {Последовательно перебираем все элементы в текущем (j-м) столбце} {матрицы.} {Если текущий элемент больше максимального, то его и объявляем} {максимальным.} {Если текущий элемент меньше минимального, то его и объявляем} {минимальным.} {Если текущий элемент меньше минимального, то его и объявляем} {минимальным.} begin {Предполагаем, что число N - простое (pr=true).} {Проверяем, делится ли число N на какое либо из чисел от 2 до N/2.} {Если встречается число i, на которое делится N, то} {число N не является простым (pr=false) и} {выходим из цикла.} {Имени функции присваиваем значение переменной pr.} end ;
{Процедура udal удаляет из массива x элементы, которые встречаются} {более одного раза. Х, N являются параметрами-переменными, так как эти} {значения возвращаются в головную программу при вызове процедуры udal.} procedure u d a l ( var x : massiv ; var n : word ) ;
var i, j,m: word ;
begin {Просматриваем все элементы, начиная с первого i=1,2,...,N;i-й элемент} {сравниваем с последующими j=i+1,i+2,...,n.} while ( imax then {Если A[I,j]> max, то} {количество максимумов равно 1, т. к. встретился наибольший в данный} {момент элемент.} {В переменную max записываем A[i,j],} {в mas[k] записываем номер строки, где хранится число A[i,j]} {Если A[i,j]=max (встретился элемент, равный максимуму), то} i f A[ i, j ]=max then {количество максимумов увеличиваем на 1,} {в mas[k] записываем номер строки, где хранится число A[i,j].} {Если в pr осталось значение false,то выводим сообщение, что в матрице} {нет простых чисел,} i f not Pr then writeln ( ’ В матрице A нет простых чисел ’ ) begin {иначе удаляем из массива mas номера строк, где хранятся максимумы,} {повторяющиеся элементы.} {Перебираем все строки матрицы.} {Если номер строки присутствует в массиве mas,} {то переписываем строку в массив b,} {упорядочиваем массив b по возрастанию.} {Упорядоченный массив записываем на место i-й строки матрицы A.} writeln ( ’ Преобразованная матрица A ’ ) ;
end.
Результаты работы программы приведены на рис. 6.35.
Матрица А Преобразованная матрица А Авторы рекомендуют читателю по рассмотренным алгоритмам и консольным приложениям задач 6.7 6.11 разработать визуальные приложения, аналогичные тем, которые были разработаны для задач 6.2 и 6.6.
В завершении этой главы рассмотрим динамические матрицы.
6.3 Динамические матрицы Понятие динамического массива можно распространить и на матрицы. Динамическая матрица представляет собой массив указателей, каждый из которых адресует одну строку (или один столбец).
Рассмотрим описание динамической матрицы. Пусть есть типы данных massiv и указатель на него din_massiv.
type massiv=array [ 1.. 1 0 0 0 ] of r e a l ;
din_massiv=^massiv ;
Динамическая матрица X будет представлять собой массив указателей.
Var X: array [ 1.. 1 0 0 ] of din_massiv ;
Работать с матрицей надо следующим образом:
1) Определить её размеры (пусть N число строк, M число столбцов).
2) Выделить память под матрицу.
f or i :=1 to N do Каждый элемент статического массива X[i] указатель на динамический массив, состоящий из M элементов типа real. В статическом массиве Х находится N указателей.
1) Для обращения к элементу динамической матрицы, расположенному в iй строке и j-м столбце, следует использовать конструкцию языка Турбо Паскаль X[i][j].
2) После завершения работы с матрицей необходимо освободить память.
f or i :=1 to N do Рассмотрим работу с динамической матрицей на следующем примере.
ЗАДАЧА 6.12. В каждой строке матрицы вещественных чисел B(N, M ) упорядочить по возрастанию элементы, расположенные между максимальным и минимальным значениями.
Алгоритмы упорядочения рассматривались в главе 5, основные принципы работы с матрицами в предыдущих параграфах текущей главы, поэтому в комментариях к тексту программы основное внимание уделено особенностям работы с динамическими матрицами.
{Описываем тип данных massiv как массив 1000 вещественных чисел.} type massiv=array [ 1.. 1 0 0 0 ] of r e a l ;
{Указатель на массив.} din_massiv=^massiv ;
{Тип данных matrica - статический массив указателей, каждый элемент} {которого является адресом массива вещественных чисел.} m a t r i c a=array [ 1.. 1 0 0 ] of din_massiv ;
var Nmax, Nmin, i, j, n,m, k : word ;
{Описана динамическая матрица b.} b : matrica ;
begin {Вводим число строк N и число столбцов M.} write ( ’N= ’ ) ; readln (N ) ;
write ( ’M ’ ) ; readln (M) ;
{Выделяем память под матрицу вещественных чисел размером N на M.} f or i :=1 to N do { Вводим Матрицу B. } writeln ( ’ Matrica B ’ ) ;
f or i :=1 to N do f or j :=1 to M do {В каждой строке находим максимальный, минимальный элементы и их номера} {и элементы, расположенные между ними, упорядочиваем "методом} {пузырька".} f or i :=1 to N do begin {Поиск минимального, максимального элемента в i-й строке матрицы и их} {номеров.} max:=b [ i ] ^ [ 1 ] ;
Nmax: = 1 ;
min:=b [ i ] ^ [ 1 ] ;
Nmin : = 1 ;
f or j :=2 to M do begin i f b [ i ] ^ [ j ]>max then begin max:=b [ i ] ^ [ j ] ;
nmax:= j end ;
begin end ;
j := j +1;
end ;
end ;
{ Выводим преобразованную матрицу. } writeln ( ’ Упорядоченная матрица B ’ ) ;
f or i :=1 to N do begin f or j :=1 to M do writeln end ;
{ Освобождаем память. } f or i :=1 to N do end.
Результаты работы программы представлены на рис. 6.36.
Динамическая матрица может быть достаточно большой, фактически её размер ограничен только объемом свободной памяти.
В заключении главы приведём задачи для самостоятельного решения.
Матрица B Упорядоченная матрица В 6.4 Задачи для самостоятельного решения 1) Определить номера строки и столбца максимального простого числа прямоугольной матрицы A(n, m). Подсчитать количество нулевых элементов матрицы и напечатать их индексы.
2) Найти среднее геометрическое элементов квадратной матрицы X(n, n), находящихся по периметру этой матрицы и на её диагоналях, если это возможно. Если среднее геометрическое вычислить невозможно, то поменять местами максимальный и минимальный элементы матрицы.
3) Сформировать вектор D, каждый элемент которого представляет собой среднее арифметическое значение элементов строк матрицы C(k, m), и вектор G любой его компонент должен быть равен произведению элементов соответствующего столбца матрицы C.
4) Задана матрица A(n, m), в каждом столбце которой максимальный элемент необходимо заменить произведением отрицательных элементов этого же столбца.
5) Задана матрица A(n, n). Определить максимальный элемент среди элементов матрицы, расположенных выше главной диагонали, и минимальный элемент среди тех, что находятся ниже побочной диагонали. После этого выполнить сортировку каждого столбца матрицы по возрастанию.
6) Заменить строку матрицы P (n, m) с минимальной суммой элементов на строку, где находится максимальный элементы матрицы.
7) Переместить максимальный элемент матрицы F (k, p) в правый верхний угол, а минимальный элемент в левый нижний.
8) Проверить, является ли матрица A(n, n) диагональной (все элементы нули, кроме главной диагонали), единичной (все элементы нули, на главной диагонали только единицы) или нулевой (все элементы нули).
9) Сформировать из некоторой матрицы A(n, n) верхнетреугольную матрицу B(n, n) (все элементы ниже главной диагонали нулевые), нижнетреугольную матрицу C(n, n) (все элементы выше главной диагонали нулевые) и диагональную матрицу D(n, n) (все элементы нули, кроме главной диагонали).
10) Заданы матрицы A(m, n) и B(n, m). Найти матрицу C = (AB)4.
11) Проверить, является ли матрица B(n, n) обратной к A(n, n). Произведением матриц A и B в этом случае должна быть единичная матрица.
12) Определить количество простых чисел, расположенных вне диагоналей матрицы B(n, n).
13) Проверить, лежит ли на главной диагонали максимальный отрицательный элемент матрицы A(n, n).
14) Переписать простые числа из матрицы A в массив B. Массив упорядочить по убыванию.
15) Переписать положительные числа из матрицы целых чисел A в массив B.
Из массива B удалить числа, в двоичном представлении которых единиц больше, чем нулей.
16) Заданы четыре квадратные матрицы: A(n, n), B(n, n), C(n, n), D(n, n), в которых хранятся целые числа. Найти матрицу, в которой находится максимальное простое число.
17) Заданы четыре квадратные матрицы; A(n, n), B(n, n), C(n, n), D(n, n), в которых хранятся целые числа. Найти матрицы, в которых на диагоналях есть простые числа.
18) Заданы три прямоугольные матрицы: A(n, m), B(r, p), C(k, q). Найти матрицы, в которых по периметру расположены только отрицательные числа.
19) Проверить, лежит ли на побочной диагонали минимальный положительный элемент матрицы A(n, n).
20) Заданы матрицы D(n, n), A(m, n) и B(n, m). Найти матрицу C = BA. Проверить, является ли матрица C(n, n) обратной к D(n, n). Произведением матриц C и D в этом случае должна быть единичная матрица.
21) Заданы четыре квадратные матрицы: A(n, n), B(n, n), C(n, n), D(n, n), в которых хранятся целые числа. Найти, в какой из матриц на побочной диагонали есть числа, состоящие из восьмёрок.
22) Заменить столбец матрицы P (n, m) с максимальной суммой элементов на столбец, где находится максимальное число, состоящее из единиц.
23) Заданы четыре квадратные матрицы: A(n, n), B(n, n), C(n, n), D(n, n), в которых хранятся целые числа. Определить, есть ли среди них матрицы, в которых на побочной диагонали находятся только числа, состоящие из единиц и двоек.
24) Переписать простые числа из матрицы целых чисел A в массив B. Из массива B удалить числа, расположенные между максимальным и минимальным элементами.
25) В матрице целых чисел A(n, n) упорядочить те строки, в которых диагональные элементы не содержат семёрок.
Глава Обработка файлов средствами Free Pascal В данной главе будут рассмотрены возможности языка Free Pascal для работы с файлами. По ходу изложения материала читатель также познакомится с компонентами Lazarus, предназначенными для выбора файла.
Предварительно давайте познакомимся с типами файлов.
7.1 Типы файлов в Free Pascal Ввод данных с клавиатуры удобен при обработке небольших объёмов информации. В случае обработки массивов, состоящих из сотен элементов, использовать ввод данных с клавиатуры нерационально. В подобных случаях данные удобно хранить в файлах. Программа будет их считывать, обрабатывать и выводить результаты на экран или в файл. Рассмотрим, как это можно сделать.
C точки зрения программиста все файлы можно разделить на три класса:
• типизированные;
• бестиповые;
• текстовые.
Файлы, состоящие из компонентов одного типа (целые, вещественные, массивы и т. д.), число которых заранее не определено и может быть любым, называются типизированными. Они заканчиваются специальным символом конец файла, хранятся в двоичном виде, содержимое подобных файлов нельзя просмотреть обычным текстовым редактором, для их просмотра нужно писать специальную программу.
В бестиповых файлах информация считывается и записывается блоками определённого размера. В подобных файлах хранятся данные любого вида и структуры.
Текстовые файлы состоят из любых символов. При записи информации в текстовый файл все данные преобразуются к символьному типу, в котором и хранятся. Просмотреть данные в подобном файле можно с помощью любого текстового редактора. Информация в текстовом файле хранится построчно. В конце кажГлава 7. Обработка файлов средствами Free Pascal дой строки хранится специальный символ конец строки. Конец самого файла обозначается символом конец файла.
Для работы с файлами в программе следует описать файловую переменную.
Для работы с текстовым файлом файловая переменная (например f) описывается с помощью служебного слова text.
var f : text ;
Для описания типизированных 1 файлов можно описать файловую переменную следующим образом:
Бестиповый файл описывается с помощью служебного слова file.
Рассмотрим несколько примеров описания файловых переменных.
type massiv=array [ 1.. 2 5 ] of r e a l ;
var a : text ; {Файловая переменная a для работы с текстовым файлом.} b : f f ; {Файловая переменная f для работы с файлом вещественных чисел.} c : f i l e of integer ; {Файловая переменная c для работы с файлом целых} {чисел.} d : f i l e of massiv ; {Файловая переменная d для работы с типизированным} {файлом, элементами которого являются массивы из 25 вещественных чисел.} Рассмотрим последовательно работу с файлами каждого типа.
7.2 Работа с типизированными файлами Знакомство с методами обработки типизированных файлов начнём с подпрограмм, которые являются общими для всех типов файлов.
7.2.1 Процедура AssignFile Для начала работы с файлом необходимо связать файловую переменную в программе с файлом на диске. Для этого используется процедура AssignFile(f,s), где f имя файловой переменной, а s полное имя файла на диске (файл должен находиться в текущем каталоге при условии, что к нему специально не указывается путь).
Рассмотрим примеры использования AssignFile для различных операционных систем.
1 Типизированные файлы иногда называют компонентными.
var begin //Пример процедуры assign для ОС Windows.
AssignFile ( f, ’ d : \ tp \tmp\ abc. dat ’ ) ;
//Пример процедуры assign для ОС Linux.
AssignFile ( f, ’ /home/ e v g e n i y / p a s c a l /6/ pr1 / abc. dat ’ ) ;
7.2.2 Процедуры reset, rewrite После установления связи между файловой переменной и именем файла на диске нужно открыть файл, воспользовавшись процедурами reset или rewrite.
Процедура reset(f), где f имя файловой переменной, открывает файл, связанный с файловой переменной f, после чего становится доступным для чтения первый элемент, хранящийся в файле. Далее можно выполнять чтение и запись данных из файла.
Процедура rewrite(f) создаёт пустой файл (месторасположение файла на диске определяется процедурой AssignFile) для последующей записи в него данных.
Если файл, связанный с файловой переменной f, существовал на диске, то после выполнения процедуры rewrite вся информация 7.2.3 Процедура СloseFile Процедура CloseFile(f), где f имя файловой переменной, закрывает файл, который ранее был открыт процедурами rewrite или reset.
Процедуру CloseFile(f) следует обязательно использовать при закрытии файла, в который происходила запись данных.
Дело в том, что процедуры записи в файл не обращаются непосредственно к диску, они записывают информацию в специальный участок памяти, называемый буфером файла. После того как буфер заполнится, вся информация из него переносится в файл. При выполнении процедуры closefile сначала происходит запись буфера файла на диск, и только потом файл закрывается. Если его не закрыть вручную, то закрытие произойдёт автоматически при завершении работы программы. Однако при автоматическом закрытии файла информация из буфера файла не переносится на диск, и, как следствие, часть информации может пропасть.
После записи информации в файл, его обязательно закрывать с помощью процедуры CloseFile. Однако при чтении данных из файла нет необходимости в обязательном закрытии файла.
7.2.4 Процедура rename Переименование файла, связанного с файловой переменной f, осуществляется в то время, когда он закрыт, при помощи процедуры rename(f,s), где f файловая переменная, s новое имя файла (строковая переменная).
7.2.5 Процедура erase Удаление файла, связанного с переменной f, выполняется посредством процедуры erase(f), в которой f также является именем файловой переменной.
Для корректного выполнения этой операции файл должен быть закрыт.
7.2.6 Функция eof Функция eof(f) (end of le), где f имя файловой переменной, принимает значение истина (true), если достигнут конец файла, иначе ложь (false).
С помощью этой функции можно проверить, достигнут ли конец файла и можно ли считывать очередную порцию данных.
7.2.7 Чтение и запись данных в файл Для записи данных в файл можно использовать процедуру write:
write ( f, x ) ;
здесь • f имя файловой переменной, • x, x1, x2,..., xn имена переменных, значения из которых записываются Тип компонентов файла обязательно должен совпадать с типом переменных.
При выполнении процедуры write значения x1, x2,..., xn последовательно записываются в файл (начиная с текущей позиции), связанный с файловой переменной f.
Для чтения информации из файла, связанного с файловой переменной f, можно воспользоваться процедурой read:
read ( f, x ) ;
здесь • f имя файловой переменной, • x, x1, x2,..., xn имена переменных, в которые считываются значения из Процедура read последовательно считывает компоненты из файла, связанного с файловой переменной f, в переменные x1, x2,..., xn. При считывании очередного значения доступным становится следующее. Следует помнить, что процедура read не проверяет, достигнут ли конец файла. За этим нужно следить с помощью функции eof.
Для того чтобы записать данные в файл, необходимо выполнить следующее:
1) Описать файловую переменную.
2) Связать её с физическим файлом (процедура AssignFile).
3) Открыть файл для записи (процедура rewrite).
4) Записать данные в файл (процедура write).
5) Обязательно закрыть файл (процедура CloseFile).
Рассмотрим создание компонентного файла на примере решения следующей несложной задачи.
ЗАДАЧА 7.1. Создать типизированный файл и записать туда n вещественных чисел.
Алгоритм решения задачи состоит из следующих этапов:
1) Открыть файл для записи с помощью оператора rewrite.
2) Ввести значение n.
3) В цикле (при i, меняющемся от 1 до n) ввести очередное вещественное число a, которое сразу же записать в файл с помощью процедуры write.
4) Закрыть файл с помощью процедуры closefile.
Текст консольного приложения Lazarus, предназначенного для решения данной задачи, приведён ниже.
program pr1 ;
{$mode objfpc}{$H+} uses Classes, SysUtils {you can add units after this} ;
begin //Связываем файловую переменную с файлом на диске.
//Открываем пустой файл для записи.
//Определяем количество элементов в файле.
//В цикле вводим очередной элемент и записываем его в файл.
//Закрываем файл.
//При записи данных в файл это делать обязательно!!!
end.
ЗАДАЧА 7.2. В папке /home/evgeniy/pascal/6/pr2/ находятся файлы вещественных чисел с расширением dat. В выбранном пользователем файле удалить все числа, меньшие среднего арифметического, расположенные между максимальным и минимальным элементами.
В задаче предполагается выбор пользователем файла для обработки. Для этого понадобится специальный компонент для выбора файла. Решение задачи 7. начнём со знакомства с компонентом OpenDialog. Этот компонент предназначен для создания стандартного диалогового окна выбора файла и расположен первым на странице Dialogs (см. рис. 7.1).
Среди основных свойств этого компонента можно выделить: