Procedure TForm1.Edit3KeyPress
(Sender: TObject; var Key: Char);
begin
if Length(Edit3.Text) = 0 then begin
if not (key in ['+','-','*','/']) then key:=#0;
end else key:=#0;
end;
procedure TForm1.Edit1Exit(Sender: TObject);
begin
a:=StrToInt(Edit1.Text);
end;
procedure TForm1.Edit2Exit(Sender: TObject);
begin
b:=StrToInt(Edit2.Text);
end;
procedure TForm1.Edit3Exit(Sender: TObject);
var С:Char;
begin
if Length(Edit3.Text) > 0 then begin
С:=Edit3.Text[1];
case С of
'+': Operat:=Plus;
'-': Operat:=Minus;
'*': Operat:=Mult;
'/': Operat:=Divide;
end;
end else Operat:=None;
end;
procedure TForm1.Button1Click(Sender: TObject);
var Z:Real;
begin
Z := 0.0;
case Operat of
Plus: Z:=a+b;
Minus: Z:=a-b;
Mult: Z:=a*b;
Divide:
if b=0 then begin
Edit4.Text:='Деление на 0';
Exit;
end else
Z := a/b;
None: begin
Edit4.Text := 'Операции нет';
Exit;
end;
end;
Edit4.Text := FormatFloat('0.00',Z);
end;
End.
Необходимо отметить, что обработчик OnKeyPress для Edit3 содержит объявление переменной C символьного типа для записи вводимого знака арифметической операции, так как непосредственное использование строки Edit3.Text в качестве селектораоператора Case запрещено. Кроме того, в этом обработчике с помощью функции Length(S) (длина строки S) проверяется, был ли осуществлен ввод символа.
Обработчик событий OnKeyPress для Edit3 усложнён, – разрешается ввод только одного символа. Дополнительные ключевые слова begin и end (составной оператор) в этом обработчике необходимы, чтобы снять неоднозначность использования else – к какому ifданное ключевое слово else относится. Кроме указанного выше составного оператора, следует обратить внимание на другие составные операторы данной программы.
В обработчике Button1Click используется процедура Exit для непосредственного выхода из обработчика. Обработчик для Edit1 одновременно используется и для Edit2, как и в предыдущем примере 2. Кроме того, следует обратить внимание, что, кроме цифровых символов, этот обработчик разрешает ввод символа минус для отрицательных чисел.
ИСПОЛЬЗОВАНИЕ ENTER В ПРИМЕРЕ 3
Часто при окончании ввода в элементе редактирования и переходе к следующему управляющему элементу оказывается более удобным использование клавиши Enter вместо Tab. Рассмотрим этот вопрос на примере 3.
Стандартным подходом в данном случае при обнаружении нажатия клавиши Enter является подход с применением передачи фокуса c помощью метода SetFocus или свойства ActiveControl. Все, что нужно сделать в примере 3, - переписать обработчики OnKeyPress в следующем виде:
procedure TForm1.Edit1KeyPress(Sender: TObject;
var Key: Char);
begin
if key=#13 then
if Sender = Edit1 then Form1.ActiveControl:=Edit3
else Button1.SetFocus;
if not (key in ['0'..'9','-', #8]) then key:=#0;
end;
Procedure TForm1.Edit3KeyPress
(Sender: TObject; var Key: Char);
begin
if key=#13 then Form1.ActiveControl:=Edit2;
if Length(Edit3.Text) = 0 then begin
if not (key in ['+','-','*','/']) then key:=#0;
end else key:=#0;
end;
Другим подходом является применение процедуры SelectNext, которая может передавать фокус следующему или предыдущему управляющему элементу. Рассматриваемая процедура имеет следующее описание: SelectNext(CurControl:
TwinControl; GoForward, CheckTabStop: Boolean),где CurControlуказывает на оконный управляющий элемент, относительно которого выполняется передача фокуса; параметр GoForward определяет направление передачи фокуса: следующий элемент (True) или предыдущий (False); параметр CheckTabStop определяет, нужно ли учитывать значение свойства TabStop управляющего элемента, который должен получить фокус. Применение данной процедуры может оказаться проще, чем предыдущий подход. Например, перепишем первый оператор в обработчике Edit1KeyPress примера 3:
if key=#13 then SelectNext(Sender as TWinControl,
true,true);
В данном случае Sender имеет тип Tobject, а необходим тип TWinControl, поэтому выполнено явное преобразование типов с помощью операции as. В заключение одно замечание. Чтобы был осуществлен правильный переход от Edit2 к Button1 (а не к Edit4), необходимо установить для Edit4 свойство TabStop=true.
Возможен еще один подход обработки нажатия клавиши Enter с помощью SelectNext – можно использовать обработчик OnKeyPress самой формы. Для этого необходимо установить свойство формы KeyPreview=true, чтобы обработчик формы получил сообщение о нажатии клавиш первым.
ОПЕРАТОРЫ ЦИКЛА
Операторы цикла используются для программирования циклических вычислительныз процессов. Циклический вычислительный процесс представляет собой неоднократно повторяющиеся вычисления при различных значениях исходных данных. Однократное выполнение расчета внутри цикла называется итерацией. Например, пусть требуется построить график Y = f(X) на отрезке [a;b] с шагом H. Для этого необходимо провести циклические, повторяющиеся вычисления в соответствии с табл. 10.
Таблица 10
Пример циклических вычислений
X | f(X) | Число итераций |
a | Y = f(a) | |
a+h | Y = f(a+h) | |
. . . . . | . . . . . . . . | |
b | Y = f(b) |
Существуют три оператора цикла: операторы For, While и Repeat.
ОПЕРАТОР ЦИКЛА FOR
Оператор For позволяет организовать выполнение какого-либо другого оператора заранее заданное число раз. Существуют 2 варианта этого оператора:
A. For <управляющая переменная> := <Start> to <Finish> do <оператор>;
B. For <управляющая переменная> := <Start> downto <Finish> do <оператор>;
Управляющая переменная позволяет ограничить количество расчетов заданного оператора, которое зависит от значений выражений <Start>и <Finish>. Выражение <Start> определяет начальное значение управляющей переменной, выражение <Finish> – конечное значение. При каждой итерации управляющая переменная увеличивается на единицу в операторе А и уменьшает своё значение на единицу в операторе B. Как только значение управляющей переменной превзойдет значение <Finish> в операторе A (станет меньше в операторе B), цикл прекращается. Выражения <Start> и <Finish> должны возвращать значения порядкового типа - соответственно <управляющая переменная> должна также иметь порядковый тип. Например,
FOR I:= 1 TO 10 DO Y:=sin(X);
FOR I:= 10 DOWNTO 1 DO Y:=sin(X);
Если в самом начале цикла значение <Finish>будет больше, чем значение <Start> для оператора А или значение <Finish>меньше, чем <Start> для оператора B, то не будет выполнено ни одной итерации.
В Delphi скорость выполнения циклических вычислений оптимизирована, поэтому имеют место ограничения на управляющую переменную:
· должна иметь порядковый тип;
· должна быть объявлена в том же блоке, в котором помещен цикл, т. е. должна быть локальной.
ПРИМЕР 4
Пусть требуется рассчитать табл. 10 и построить на экране дисплея график y = f(x) = sin(x). Шаг изменения координат x и y графика зададим в пикселах и пусть, h = 1. В данном случае имеют место два типа графиков: физический и дисплейный. Пусть X, Y, Н - координаты и шаг физического графика, x, y, h - координаты и шаг дисплейного графика. Прежде, чем рисовать график, необходимо выполнить преобразование физических координат в дисплейные (или масштабирование) так, чтобы дисплейный график верно отражал процесс, смоделированный физическим графиком. Физический график начинается в точке x = a и заканчивается в точке x = b. Пусть дисплейный график начинается в точке X = xn и продолжается до точки X = xk (рис. 16). Выберем следующую схему построения дисплейного графика:
1) Используя заданное h, находим текущее значение x;
2) Рассчитываем H и находим значение физической переменной X;
3) Определяем Y;
4) Преобразуем Y в y;
5) На экране дисплея отображаем полученную точку x и y.
|
Введем масштаб my. и запишем соотношение y = my×Y. Так как данное соотношение справедливо для любой точки графика, необходимо, чтобы оно выполнялось и при значении ymax = my× Ymax(все значения y будут воспроизводиться на экране). Из этого соотношения можно определить масштаб my, т.е. . Максимальное значение Ymax в данном случае для функции sin(X) равно 1, а для дисплейного графика ymax задаётся, в общем случае, произвольно.
Теперь найдём соотношение между x и X (или между h и H, что одно и то же). Примем, чтобы количество точек по оси абсцисс для дисплейного и физического графиков было одинаковым. Чтобы это выполнялось, необходимо согласовать значение шага H для физического графика с выбранным h для дисплейного графика. Запишем
,
откуда следует , обозначив ,
получим требуемое соотношение H = mx×h.
Для размещения графика понадобится компонент TImage (страница Additional). Форма примера 4 приводится на рис. 17. Какие объекты используются в данном примере, можно найти в тексте программы.
Рис. 17. Форма к примеру 4.
Компонент TImage используется для рисования графических объектов и геометрических фигур. Полезным свойством компонента TImage является Canvas (холст), которое включает, в частности, свойства Pen, Brush. Канва имеет методы: MoveTo (используется для перемещения невидимого графического курсора в заданную точку) и LineTo(позволяет рисовать линии). Эти два метода используются для рисования графика на канве TImage. Координаты задаются в пикселах, горизонтальная ось направлена слева направо, вертикальная - сверху вниз. Вариант решения задачи приводится на рис. 18.
Рис. 18. Результат решения примера 4.
Выбраны следующие размеры для Image1: Width = 305, Height = 154. Ось абсцисс проведена при значении y0 = 75. Текст программы приведен ниже.
unit prim4;
Interface
uses Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, Buttons,
ExtCtrls;
Type TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
BitBtn1: TBitBtn;
Edit1: TEdit;
Label1: TLabel;
Edit2: TEdit;
Label2: TLabel;
Image1: TImage;
Label3: TLabel;
Bevel1: TBevel;
procedure Edit1KeyPress(Sender: TObject;
var Key: Char);
procedure Button1Click(Sender: TObject);
end;
var Form1: TForm1;
Implementation
{$R *.DFM}
procedure TForm1.Edit1KeyPress(Sender: TObject;
var Key: Char);
begin
if not (key in ['0'..'9','-','.',#8]) then key:=#0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var a,b:real;
xn,xk,y0,yk:integer;
mx,my:real;
x,y,ymax:real;
i:integer;
Begin
xn:=15;
xk:=280;
y0:=75;
yk:=70;
a:=StrToFloat(Edit1.Text);
b:=StrToFloat(Edit2.Text);
ymax:=1.0;
{Зададим цвет и толщину пера}
Image1.Canvas.Pen.Color:=clBlack;
Image1.Canvas.Pen.Width:=1;
{Закрасим цветом фона предыдущий график}
Image1.Canvas.Brush.Color:=clWhite;
Image1.Canvas.Rectangle(0,0,Image1.Width,
Image1.Height);
{Рисуем оси координат}
Image1.Canvas.MoveTo(xn,y0);
Image1.Canvas.LineTo(xk,y0);
Image1.Canvas.MoveTo(xn,y0+yk);
Image1.Canvas.LineTo(xn,y0-yk);
{Проверяем, введено ли b < a}
if b>=a then begin
Label3.Caption:= 'Графика нет';
Exit;
end else Label3.Caption:= '';
{Устанавливаем цвет и толщину карандаша для графика}
Image1.Canvas.Pen.Color:=clRed;
Image1.Canvas.Pen.Width:=2;
mx:=(b-a)/(xk-xn);
my:=yk/ymax;
{Устанавливаем начальную точку графика}
Image1.Canvas.MoveTo(xn,y0-round(my*sin(a)));
{Строим график в цикле for}
for i:=1 to xk-xn do begin
x:=a+i*mx;
y:=my*sin(x);
Image1.Canvas.LineTo(xn+i,y0-round(y));
end;
end;
End.
В обработчике событий OnKeyPress учтено, что вводятся вещественные числа. Используемая функция Round(Z) преобразует вещественное число Z в целое с предварительным округлением. Для того чтобы возможно было неоднократное построение графика, необходимо стирать предыдущий график.
ОПЕРАТОР ЦИКЛА WHILE
В отличие от оператора For, оператор цикла While используется, когда заранее неизвестно количество выполняемых итераций. Этот оператор записывается так:
While <логическое выражение> do <оператор>;
Итерации выполняются до тех пор, пока логическое выражение остается истинным. Если с самого начала значение логического выражения ложно, то оператор While будет пропущен.
ПРИМЕР 5
Найти все делители целого числа X, кроме единицы и самого числа. Воспользуемся простейшим алгоритмом. Будем перебирать все делители, начиная с числа 2, и проверять, делится ли нацело заданное число X. Очевидно, последний делитель, который следует проверять, равен половине числа X. Если ни одного делителя не найдено, то число - простое. Будем исследовать только положительные числа. Воспользуемся в этом примере компонентом TListBox. Форма с вариантом решения задачи приведена на рис. 19.
Рис. 19 Форма с вариантом решения примера 5.
Интерфейсный элемент TListBox позволяет представить список объектов (свойство Items), чаще всего строк (класс TStrings), из которых можно выбрать один элемент. Если список не умещается на экране, то возможнна его прокрутка по вертикали. Список можно представлять не только в один столбец - количество столбцов задаётся свойством Columns. Если Columns > 1, то разрешается и горизонтальная прокрутка элементов. Элементы списка могут, например, динамически добавляться. При этом используется свойство Items с соответствующим методом Add(Items.Add) – добавить. Какой элемент выбран, запоминается в свойстве ItemIndex. Можно задать одновременный выбор нескольких элементов в списке с помощью свойства MultiSelect. Компонент имеет много других разнообразных свойств, что делает его удобным для отображения данных (например, свойство Sorted позволяет представлять список в алфавитном порядке и др.).
Ниже приводится программа для примера 5.
unit prim5;
Interface
uses Windows, Messages, SysUtils,
Classes,Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
Type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
BitBtn1: TBitBtn;
Bevel1: TBevel;
Label1: TLabel;
Edit1: TEdit;
ListBox1: TListBox;
Label2: TLabel;
Label3: TLabel;
procedure Edit1KeyPress(Sender: TObject;
var Key: Char);
procedure Button1Click(Sender: TObject);
end;
var Form1: TForm1;
Implementation
{$R *.DFM}
procedure TForm1.Edit1KeyPress(Sender: TObject;
var Key: Char);
begin
if not (key in ['0'..'9',#8]) then key:=#0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var x,half,divider:word;
i:byte;
begin
Label3.Caption:='';
ListBox1.Items.Clear;
x:=StrToInt(Edit1.Text);
half:=x div 2;
divider:=2;
i:=0;
While divider <= half do begin
if x mod divider = 0 then begin
inc(i);
ListBox1.Items.Add(IntToStr(i)+'-й делитель='
+ IntToStr(divider));
end;
inc(divider);
end;
if i=0 then Label3.Caption:='Число простое';
end;
End.
В отличие от оператора цикла For, который автоматически изменяет управляющую переменную и управляет циклом, в операторе While программист должен сам организовать управление, заключающееся в следующем:
ü выбрать какую-либо управляющую переменную, в данном случае i – счётчик циклов;
ü присвоить ей начальное значение, например i:= 0;
ü в операторе While записать условие продолжения выполнения цикла, например divider <= half;.(Как только это условие станет равным false, цикл закроется);
ü внутри цикла осуществлять изменение управляющей переменной на каждой итерации, например inc(i) – увеличить значение i на единицу.
ОПЕРАТОР ЦИКЛА REPEAT
Оператор цикла Repeat записывается следующим образом: Repeat
<Оператор1>;
. . . . . . . . . . .
<ОператорN>;
Until <логическое выражение>;
В этом цикле выполняются все операторы между Repeat и Until, пока логическое выражение не станет истинным. В отличие от оператора While, в котором логическое выражение определяет условие продолжения итераций, в операторе Repeat логическое выражение определяет условие окончания цикла. Независимо от значения логического выражения, хотя бы одна итерация обязательно должна выполниться. Как и в случае с циклом While, программист должен сам организовать управление итерациями в цикле Repeat.
ПРИМЕР 6
Рассчитать с точностью e сумму следующего ряда:
,
где X – какое-либо число из диапазона –1 <= X <= 1.
Точность e означает следующее. Обозначим член ряда; суммирование (вычисление Y) продолжать до тех пор, пока значение а не станет меньше e.
Вывести на экран историю расчета, т.е. какое значение на каждой итерации имели величины a и Y, в следующем виде: итерация 1 а = y = итерация 2 а = y = и т.д.
На рис. 20 построен алгоритм расчета суммы ряда. В этом алгоритме введена дополнительная переменная b для отслеживания знака очередного члена ряда.
На рис. 21 приводится форма с вариантом расчета. Расположенные на форме компоненты можно определить, исходя из текста программы, который приводится ниже.
Для решения данной задачи был использован компонент TMemo. Этот элемент управления служит для получения какого-либо текста пользователя и его отображения на экране. TMemo называют многострочным редактором (однострочным редактором является компонент TEdit.).
Доступ ко всему тексту может быть получен через свойство Text. Свойство Linesпредставляет весь текст в виде совокупности строк. Все строки в TMemo пронумерованы, начиная от нуля. Если на экране строки полностью не отображаются (задана маленькая ширина Width), то с помощью свойства WordWrap можно управлять переносом строк. TMemo имеет свойство ScrollBars – можно устанавливать полосы прокрутки. По умолчанию: ScrollBars установлено равным ssNone. Можно задать центрирование строк с помощью свойства Alignment. Свойство Lines содержит методы Add(добавить), Delete(удалить), Insert(вставить).
Рис. 21 Форма с вариантом решения примера 6.
Для Memo1 в инспекторе объектов установлено свойство WordWrap равным true, а свойство ScrollBars – ssVertical. Далее приводится программа.
unit prim6;
Interface
uses Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs,
StdCtrls, Buttons, ExtCtrls;
Type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
BitBtn1: TBitBtn;
Bevel1: TBevel;
Memo1: TMemo;
Label1: TLabel;
Label2: TLabel;
Edit1: TEdit;
Edit2: TEdit;
Label3: TLabel;
procedure Edit1KeyPress(Sender: TObject;
var Key: Char);
procedure Button1Click(Sender: TObject);
procedure Edit1Exit(Sender: TObject);
end;
var Form1: TForm1;
Implementation
{$R *.DFM}
procedure TForm1.Edit1KeyPress(Sender: TObject;
var Key: Char);
begin
if not (key in ['0'..'9','-','.',#8]) then key:=#0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var x,y,a,b,eps:real;
n:byte;
begin
Memo1.Lines.Clear;
y:=0.0;
n:=0;
b:=-1.0;
x:=StrToFloat(Edit1.Text);
eps:=StrToFloat(Edit2.Text);
Repeat
b:=-b*x;
inc(n);
a:=b/n;
y:=y+a;
Memo1.Lines.Add('Итерация'+IntToStr(n)+' a=' +
FormatFloat('0.00000',a)+' y='+
FormatFloat('0.0000',y));
Until abs(a) < eps;
end;
procedure TForm1.Edit1Exit(Sender: TObject);
begin
if Abs(StrToFloat(Edit1.Text))>1.0 then
if Application.MessageBox('Введите число X',
'Число /X/<=1', MB_OK)=IDOK then Edit1.SetFocus;
end;
End.
В приведенной программе обработчик события Button1Click написан по алгоритму рис. 20, кроме одного дополнения – внутрь цикла добавлена процедура формирования истории расчета с помощью объекта Memo1. Обработчик Edit1Exit отслеживает условие: X по модулю должно быть не больше 1. В случае невыполнения этого условия вызывается стандартное диалоговое окно объекта Application MessageBox. Синтаксически подпрограмма MessageBoxоформлена в виде функции. При вызове функции MessageBox необходимо указать три параметра:
1) текст-подсказка, что надо делать;
2) заголовок диалогового окна;
3) вариант внешнего вида окна – константа MB_OK указывает, что окно с одной кнопкой.
Значение функции вызова диалогового окна сравнивается с константой IDOK. В данном случае любой вариант ответа пользователя приводит к возврату операции ввода числа X (Edit1.SetFocus – установить фокус на объект Edit1).