Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Находится ли элемент во множестве?



Самая простая операция, для понятия смысла которой даже не нужно задумываться. Чтобы проверить, входит ли элемент во множество, следует использовать специальную конструкцию с оператором in. Слева от него указывается элемент, справа - множество. Результатом, как несложно догадаться, является логичное значение - истина или ложь. True - элемент принадлежит множеству, False - не принадлежит:

var A: set of Char;

{...}

A:=['A'..'E','X'];

if 'D' in A then ShowMessage('Элемент В находится во множестве A.');

Несложно проверить, что сообщение в данном случае появится на экране.

Объединение множеств

Если есть два множества, определённые на одном и том же типе данных, то их можно объединить и получить таким образом новое множество.

Если изобразить множества в виде кругов, причём круги пересекаются в том случае, если у множеств есть одинаковые элементы, то объединение можно изобразить следующим образом:

В словесном описании операция объединения - результирующее множество содержит все те элементы, которые есть хотя бы в одном из двух исходных множеств.

Объединение записывается знаком плюс "+". Пример:

var A,B,C: set of Char;

{...}

A:=['A','B','C'];

B:=['X','Y','Z'];

C:=A+B;

//C = ['A','B','C','X','Y','Z']

Включение одного элемента во множество делается точно таким же образом, просто в этом случае включаемое множество содержит всего один элемент.

Пересечение множеств

Операция пересечения формирует множество только из тех элементов, которые одновременное присутствуют как в первом, так и во втором исходном множестве. Операция пересечения графически:

Пересечение обозначается звёздочкой "*". Пример:

var X,Y,Z: set of Byte;

{...}

X:=[1,2,3,4,5];

Y:=[4,5,6,7,8];

Z:=X*Y;

//Z = [4,5]

Разность множеств

Операция вычитания удаляет из первого множества те элементы, которые есть во втором множестве:

Пример:

var X,Y,Z: set of Char;

{...}

X:=['A'..'D'];

Y:=['D'..'F'];

Z:=X-Y;

//Z = ['A'..'C']

Следует обратить внимание, что порядок множеств в данном случае важен, т.е. X-Y и Y-X - это разные множества.

Применение множеств

Множества находят широкое применение. С помощью множеств удобно задавать набор опций, каждая из которых либо включена, либо выключена. К примеру, поместите на форму кнопку (TButton), перейдите в инспектор объектов, разверните свойство Font (шрифт) и найдите свойство Style. Вот это свойство как раз и реализовано множеством. Во множестве 4 элемента: fsBold, fsItalic, fsUnderline и fsStrikeOut, каждый из которых отвечает за стиль шрифта. Принадлежность элементов ко множеству задаётся указанием значения True или False для каждого из этих пунктов. В строке "Style" находится описание данного множества. Попробуйте изменять стиль и посмотреть, как меняется описание множества Style.

А теперь давайте сделаем простенький интерфейс для доступа к этому свойству. Пусть будет меняться стиль шрифта у этой кнопки (Button1). Поместим на форму 4 TCheckBox - для доступа ко всем значениям и дадим им соответствующие имена. Изменение стиля будем делать при нажатии на саму эту кнопку. Пример реализации:

procedure TForm1.Button1Click(Sender: TObject);

begin

Button1.Font.Style:=[]; //Сделали множество пустым

//Теперь смотрим состояния флажков и добавляем нужные стили

if CheckBox1.Checked then Button1.Font.Style:=Button1.Font.Style+[fsBold];

if CheckBox2.Checked then Button1.Font.Style:=Button1.Font.Style+[fsItalic];

if CheckBox3.Checked then Button1.Font.Style:=Button1.Font.Style+[fsUnderline];

if CheckBox4.Checked then Button1.Font.Style:=Button1.Font.Style+[fsStrikeOut];

end;

Чтобы не повторять везде одно и то же "Button1.Font.", эту часть кода можно, что называется, вынести за скобку при помощи специального оператора with. Ранее речь о нём не шла, однако этот оператор очень удобен. Смысл его прост: то, что вынесено вперёд, автоматически применяется ко всему, что находится внутри данного блока. В нашем случае будет так:

procedure TForm1.Button1Click(Sender: TObject);

begin

with Button1.Font do

begin

Style:=[]; //Сделали множество пустым

//Теперь смотрим состояния флажков и добавляем нужные стили

if CheckBox1.Checked then Style:=Style+[fsBold];

if CheckBox2.Checked then Style:=Style+[fsItalic];

if CheckBox3.Checked then Style:=Style+[fsUnderline];

if CheckBox4.Checked then Style:=Style+[fsStrikeOut];

end

end;

Согласитесь, так гораздо удобнее. Используйте оператор with как можно чаще - с его помощью и код по объёму становится меньше и скорость работы увеличивается.

 

 

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

 

У большинства компонент среди свойств можно найти множества. Например, у диалога открытия файла TOpenDialog (вкладка Dialogs) множеством представлено свойство Options, которое содержит приличное число элементов: Вы спросите - а что это за названия элементов у множеств? Ответ прост - это специально объявленный перечислимый тип данных, на основе которого и создано множество. Если немного покопать, то можно найти описание этого типа: TOpenOption = (ofReadOnly, ofOverwritePrompt, ofHideReadOnly, ofNoChangeDir, ofShowHelp, ofNoValidate, ofAllowMultiSelect, ofExtensionDifferent, ofPathMustExist, ofFileMustExist, ofCreatePrompt, ofShareAware, ofNoReadOnlyReturn, ofNoTestFileCreate, ofNoNetworkButton, ofNoLongNames, ofOldStyleDialog, ofNoDereferenceLinks, ofEnableIncludeNotify, ofEnableSizing, ofDontAddToRecent, ofForceShowHidden); TOpenOptions = set of TOpenOption; Как видите, ничего сверхестественного здесь нет - вам всё уже знакомо.

 


 

Тема. Динамический массив Выделение и освобождение динамической памяти

Задание: Составить конспект.

План работы:

1 Ознакомиться с перечнем вопросов, подлежащих рассмотрению

2 Ознакомиться с представленным теоретическим материалам

3 Ответить на вопросы для самопроверки

4 Законспектировать ответы на вопросы, подлежащие рассмотрению, привести примеры

Включайте в конспект не только основные положения, но и обосновывающие их выводы, конкретные факты и примеры (без подробного описания).

5.Составляя конспект, записывайте отдельные слова сокращённо, выписывайте только ключевые слова, делайте ссылки на страницы конспектируемой литературы, применяйте условные обозначения.

6.Чтобы форма конспекта отражала его содержание, располагайте абзацы «ступеньками», подобно пунктам и подпунктам плана, применяйте разнообразные способы подчеркивания, используйте карандаши и ручки разного цвета.

 

Вопросы для самостоятельной работы

1 Объявление массива

2 Выделение памяти под массив

3 Операции с массивами

3 Освобождение занимаемой памяти

 

Вопросы для самоконтроля:

1 В чем отличие статических и динамических массивов

2 Какая процедура используется для выделения памяти

3 Можно ли выполнять действия над массивом целиком

4 Какая процедура используется для освобождения памяти

 

Форма контроля: Оценка составленного конспекта.

 


Теоретический материал

Динамические массивы в Delphi не имеют фиксированного размера . Чтобы объявить такой массив необходимо записать:

 

var da_MyArray : array of integer;

 

Как видим , мы просто говорим Delphi, что нам нужен одномерный массив типа Integer, а об его размере мы скажем когда нибудь потом.

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

2. Для выделения памяти для динамического массива в Delphi используется процедура SetLength: SetLength(da_MyArray,20);

После вызова этой процедуры будет выделена память для 20 элементов массива, которые будут проиндексированы от 0 до 19 (обратите внимание: индексирование начинается с нуля, а не с единицы!). После этого можно работать с динамическим массивом- присваивать ему значения, производить с элементами различные вычисления, вывод на печать и т.д.

Например

da_MyArray[0] := 5 ; da_MyArray[9] := 9 ;

da_MyArray[1] := da_MyArray[0]+ da_MyArray[9] ;

 

3. Как только динамический массив был распределен, вы можете передавать массив стандартным функциям Length, High, Low и SizeOf Функция Length возвращает число элементов в динамическом массиве, High возвращает самый высокий индекс массива (то есть Length - 1), Low возвращает 0. В случае с массивом нулевой длины наблюдается интересная ситуация: Highвозвращает -1, а Low - 0, получается, что High меньше Low. :) Функция SizeOf всегда возвращает 4 - длина в байтах памяти указателя на динамический массив

iHigh := High (da_MyArray3);
iLow := Low (da_MyArray3);

iLength := Length (da_MyArray3);

iSizeOf := SizeOf (da_MyArray3);

3. Доступ к данным динамических массивов с помощью низкоуровневых процедур типа ReadFile или WriteFile , или любых других подпрограмм, получающих доступ сразу ко всему массиву, часто выполняется неправильно. Для обычного массива (его часто называют также статическим массивом - в противоположность динамическому массиву) переменная массива тождественна его данным. Для динамического массива это не так - переменная это указатель. Так что если вы хотите получить доступ к данным динамического массива - вы не должны использовать саму переменную массива, а использовать вместо неё первый элемент массива.

правильно

WriteFile(FHandle, da_MyArray02[0], Length(da_MyArray02), dwTemp, nil )

неправильно

WriteFile(FHandle, da_MyArray02, Length(da_MyArray02), dwTemp, nil)

5. Рассмотрим пример присваивания динамических массивов одного другому

var da_A,da_B: array of integer;


Begin

SetLength(da_A,2);

SetLength(da_B,2);

da_A[0]:=2;

da_B[0]:=3;

da_A:=da_B;

da_B[0]:=4;

end;

 

После этих манипуляций da_A[0] равно 4. Дело в том , что при присвоении da_A:=da_B не происходит копирование т.к. da_A, da_B, это всего лишь указатели на область памяти. Для копирования необходимо использовать функцию Copy.

6. Рассмотрим пример копирования динамических массивов с использованием функции Copy

Var  da_A,da_B: array of integer;

Begin

SetLength(da_A,2);

SetLength(da_B,2);

da_A[0]:=2;

da_B[0]:=3;

da_A:=Copy (da_B);

da_B[0]:=4;

end;

 

После этих манипуляций da_A[0] равно 3. После функции Copyda_A и da_B указывают на разные области памяти, поэтому при изменении da_B в da_A ничего не происходит и его значения остаются неизменными.

7. Динамические массивы (например, array of Integer) в Delphi в памяти расположены следующим образом. Библиотека runtime добавляет специальный код, который управляет доступом и присваиваниями. В участке памяти ниже адреса, на который указывает ссылка динамического массива, располагаются служебные данные массива: два поля - число выделенных элементов и счётчик ссылок (reference count).

Расположение динамического массива в памяти

 

Если, как на диаграмме выше, N - это адрес в переменной динамического массива, то счётчик ссылок массива лежит по адресу N - 8, а число выделенных элементов (указатель длины) лежит по адресу N - 4. Первый элемент массива (сами данные) лежит по адресу N. Для каждой добавляемой ссылки (т.е. при присваивании, передаче как параметр в подпрограмму и т.п.) увеличивается счётчик ссылок, а для каждой удаляемой ссылки (т.е. когда переменная выходит из области видимости или при переприсваивании или присваивании nil) счётчик уменьшается.

8. Программы, которые иллюстрируют теоретические сведения по динамическим массивам.

 

 


Окно программы пример 1                                    Окно программы пример 2

Тема. Процедуры и функции для работы с файлами.

Задание: Составить конспект.

План работы:

1 Ознакомиться с перечнем вопросов, подлежащих рассмотрению

2 Ознакомиться с представленным теоретическим материалам

3 Ответить на вопросы для самопроверки

4 Законспектировать ответы на вопросы, подлежащие рассмотрению, привести примеры

Включайте в конспект не только основные положения, но и обосновывающие их выводы, конкретные факты и примеры (без подробного описания).

5.Составляя конспект, записывайте отдельные слова сокращённо, выписывайте только ключевые слова, делайте ссылки на страницы конспектируемой литературы, применяйте условные обозначения.

6.Чтобы форма конспекта отражала его содержание, располагайте абзацы «ступеньками», подобно пунктам и подпунктам плана, применяйте разнообразные способы подчеркивания, используйте карандаши и ручки разного цвета.

Вопросы для самостоятельной работы

1 Технология работы с файлами

2 Типы файлов

3 Процедуры и функции для работы с файлами

Вопросы для самоконтроля:

1 Опишите последовательность действий при работе с файлами

2 Что общего и в чем отличия при работе с текстовыми и типизированными файлами

3 Как задать физическое местоположение файла

4 Какая функция свидетельствует об окончании файла

5 Что общего и в чем отличия способов открытия файла

 

Форма контроля: Оценка составленного конспекта.

 



Теоретический материал

Технология работы с файлами в системе Delphi требует определённого порядка действий:

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

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

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

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

В Delphi реализовано несколько способов работы с файлами. Познакомимся со классическим способом, связанным с использованием файловых переменных. Файловая переменная вводится для указания на файл. Делается это с помощью ключевого слова File :

var F: File ;

Описанная таким образом файловая переменная считается нетипизированной, и позволяет работать с файлами с неизвестной структурой. Данные считываются и записываются побайтно блоками, размер которых указывается при открытии файла, вплоть от 1 байт.

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

var F: File of тип_записи ;

В качестве типа могут использоваться базовае типы, или создаваться свои. Важно только, чтобы для типа был точно известен фиксированный размер в байтах, поэтому, например, тип String в чистом виде применяться не может, а только в виде String[N].

Данные, считанные из файла или записываемые в файл, содержатся в обычной переменной, которая должна быть того же типа, что и файловая. Поэтому сначала в программе следует описать нужный тип, а затем ввести две переменные этого типа - файловую и обычную:

 

 


Для текстовых файлов отдельно укажу, что тип файловой переменной в этом случае TextFile, а тип обычной - String.

Для открытия файла нужно указать, где он расположен. Для этого файловая переменная должна быть ассоциирована с нужным файлом, который определяется его адресом. Адрес файла может быть абсолютным, с указанием диска и каталогов ('C:\Мои документы\Мои рисунки\FileName.ini'), или относительным, тогда он создаётся в папке с .exe файлом программы. Для задания относительного адреса достаточно указать имя файла с нужным расширением. Делается это оператором AssignFile :

AssignFile(SaveF, 'C:\Мои документы\Мои рисунки\FileName.ini');

AssignFile(SaveF, 'FileName.ini');

Теперь файл должен быть открыт.

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

Открытие файла оператором Reset откроет существующий файл к считыванию или записи данных, и его указатель будет установлен на начало файла:

Rewrite(SaveF);

Reset(SaveF);

 

Каждый из этих операторов может иметь второй необязательный параметр, имеющий смысл для нетипизированных файлов, и указывающий длину записи нетипизированного файла в байтах:

Rewrite(SaveF,1);

Reset(SaveF, 1);

Чтение файла производится оператором Read :

Read(SaveF, SaveV);

Запись в файл производится оператором Write :

Write(SaveF, SaveV);

 

При этом чтение и запись производится с текущей позиции указателя, затем указатель устанавливается на следующую запись. Можно проверить, существует ли нужный файл, оператором FileExists :

If FileExists('FileName.ini') then Read(SaveF, SaveV);

 

Принудительно установить указатель на нужную запись можно оператором Seek(SaveF, N), где N - номер нужной записи, который, как и почти всё в программировании, отсчитывается от нуля:

Seek(SaveF, 49); - установка указателя на 50-ю запись.

При последовательном чтении из файла рано или поздно будет достигнут конец файла, и при дальнейшем чтении произойдёт ошибка. Проверить, не достигнут ли конец файла, можно оператором EOF (аббревиатура End Of File), который равен true, если прочитана последняя запись и указатель находится в конце файла:

 

while (not EOF(SaveF)) do Read(SaveF, SaveV);

 

Для текстовых файлов вместо Read и Write используются операторы Readln и Writeln, умеющие определять конец строки

Оператор Truncate(SaveF) позволяет отсечь (стереть или, если хотите, удалить!) все записи файла, начиная от текущей позиции указателя, и до конца файла.

В конце работы с файлом его необходимо закрыть. Это делается оператором CloseFile(SaveF) ;

Создаём обработчик события Формы OnCreate со следующим содержимым:

 

procedure TForm1.FormCreate(Sender: TObject) ;

begin
AssignFile(SaveF, 'Init.ini') ;

if FileExists('Init.ini') then begin Reset(SaveF) ;

Read(SaveF, SaveV) ;

m1.Left := SaveV.X ;

Form1.Top := SaveV.Y ;

Form1.Caption:=SaveV.Caption ;//Наши переменные дополнительно сохраняют заголовок Формы!

end ;

end ;

 

Теперь необходимо создать обработчик события OnClose :

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction) ;



Begin

Rewrite(SaveF) ; //Нет необходимости проверять наличие файла, создадим его заново!

SaveV.X := Form1.Left ;

SaveV.Y := Form1.Top ;

SaveV.Caption := Form1.Caption ;

Write(SaveF, SaveV) ;

CloseFile(SaveF) ;

end ;

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

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

Рассмотрим компоненты, позволяющие в работающей программе осуществлять выбор файлов. Delphi диалоги выбора файла позволяют указать програме, с каким файлом мы хотим работать.

На вкладке палитры компонентов Dialogs находятся компонент Delphi OpenDialog и компонент Delphi SaveDialog. Все Delphi диалоги, находящиеся на этой вкладке, в том числе и Delphi диалоги выбора файла, невизуальные, т.е. при переносе их на Форму в работающей программе их не видно, они видны только на этапе конструирования. Компонент Delphi OpenDialog позволяет открыть в нашей программе стандартное Windows-окно диалога открытия файла, компонент Delphi SaveDialog - окно диалога сохранения.

 

Delphi диалоги выбора файла сами по себе ничего не делают, а только предоставляют настройки, сделанные пользователем при выборе файла. Самый важный метод Delphi диалогов - Execute. Он срабатывает в момент нажатия кнопки "открыть" или "сохранить" в окне выбора файла. Для примера давайте введём в программу возможность выбора файла для загрузки в редактор Memo, и сохранения после редактирования.

Итак, кидаем на Форму оба Delphi диалога, текстовый редактор Memo, и три кнопки Button. В свойство Caption одной из них записываем "Открыть...", другой - "Сохранить", третьей - "Сохранить как..."

 

 

В обработчике OnClick кнопки "Открыть..." пишем:

if OpenDialog1.Execute then Memo1.Lines.LoadFromFile(OpenDialog1.FileName);

 

В результате выбора файла свойство FileName компонента OpenDialog получает значение полного адреса выбранного файла, который мы и вставляем в функцию загрузки файла компонента Memo.

Всё это хорошо, но только в данном случае, когда записанное выражение записывается в одну строку. Если программа использует несколько раз выражение OpenDialog1.FileName, то писать руками устанешь. В Delphi для такого случая есть так называемый "оператор присоединения" with. Он используется для любых объектов, имеющих длинный "хвост" из свойств, которые приходится записывать многократно. Вот как он записывается:

with Объект do begin end;

Свойства Объекта внутри логических скобок begin/end можно записывать непосредственно. Допускается перечислять через запятую несколько объектов. Естественно, в случае, когда внутри скобок находится один оператор, они необязательны. Перепишем фрагмент загрузки файла с использованием оператора присоединения:

with OpenDialog1, Memo1 do if Execute then Lines.LoadFromFile(FileName);

 

Запись получается более компактной.

Так как свойства компонентов OpenDialog и SaveDialog одинаковы, сохранение текста выглядит абсолютно аналогично. Создаём обработчик нажатия кнопки "Сохранить как..." и пишем:

with SaveDialog1, Memo1 do if Execute then begin Lines.SaveToFile(FileName);

OpenDialog1.FileName:=FileName; // Чтобы исправленный текст не затёр источник

end;

Наконец, для кнопки "Сохранить" пишем:

Memo1.Lines.SaveToFile(OpenDialog1.FileName); // Сохраняем туда, откуда считали (В предыдущей строчке была ошибка.

При работе этих фрагментов можно заметить, что выбирать приходится из всех файлов в нужной директории. Удобнее видеть только, например, текстовые файлы, или другой тип файлов по нашему выбору. Для этого используются фильтры, свойство Filter в наших компонентах. Настраивается оно в Инспекторе Объектов. При выборе его можно перейти в редактор фильтров:

  В колонке FilterName записываем имена фильтров, в колонке Filter - список масок файлов, разделённых точкой с запятой. Маска файла в данном случае выглядит как *.расширение_файла; Звёздочка означает, что выбираются файлы с любыми именами, подходящие по расширению.

Свойство Delphi диалогов Title позволяет записать в заголовок нужную нам фразу. Если оставить его пустым, то в заголовке будут стандартные "открыть" или "сохранить"
Свойство InitialDir позволяет в момент открытия оказаться в нужной нам директории. Оно доступно как на этапе "конструирования", так и программно.

 


 

Тема. Библиотеки подпрограмм. Рекурсивные подпрограммы

Задание: Составить конспект.

План работы:

1 Ознакомиться с перечнем вопросов, подлежащих рассмотрению

2 Ознакомиться с представленным теоретическим материалам

3 Ответить на вопросы для самопроверки

4 Законспектировать ответы на вопросы, подлежащие рассмотрению, привести примеры

Включайте в конспект не только основные положения, но и обосновывающие их выводы, конкретные факты и примеры (без подробного описания).

5.Составляя конспект, записывайте отдельные слова сокращённо, выписывайте только ключевые слова, делайте ссылки на страницы конспектируемой литературы, применяйте условные обозначения.

6.Чтобы форма конспекта отражала его содержание, располагайте абзацы «ступеньками», подобно пунктам и подпунктам плана, применяйте разнообразные способы подчеркивания, используйте карандаши и ручки разного цвета.

Вопросы для самостоятельной работы

1 Понятие процедуры и функции. Общее и различия

2 Локальные и глобальные данные

3 Понятие рекурсии, в каких случаях ее применяют

Вопросы для самоконтроля:

1 Чем заголовок функции отличается от заголовка процедуры

2 В каких случаях желательно использовать процедуру, а в каких функцию и почему

3 В чем отличия локальных и глобальных данных

4 Как организовать рекурсивную подпрограмму

 

Форма контроля: Оценка составленного конспекта.

 



Теоретический материал

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

Вообще, существует методика программирования "сверху вниз". Методика программирования сверху вниз" разбивает задачу на несколько более простых, которые оформляются в виде подпрограмм. Те, в свою очередь, при необходимости также делятся до тех пор, пока стоящие перед программистом проблемы не достигнут приемлемого уровня сложности (то есть простоты!). Таким образом, эта методика программирования облегчает написание программ за счёт создания так называемого скелета, состоящего из описателей подпрограмм, которые в дальнейшем наполняются конкретными алгоритмами. Пустое описание подпрограммы иначе называется "заглушкой".

И процедуры, и функции позволяют добиться одинаковых результатов. Но разница всё же есть.

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

Функция Delphi также позволяет выполнить всё перечисленное, но дополнительно возвращает результат в присвоенном ей самой значении. То есть вызов функции может присутствовать в выражении справа от оператора присваивания. Таким образом, функция - более универсальный объект!

Описание подпрограммы состоит из ключевого слова procedure или function, за которым следует имя подпрограммы со списком параметров, заключённых в скобки. В случае функции далее ставится двоеточие и указывается тип возвращаемого значения. Обычная точка с запятой далее - обязательна! Сам код подпрограммы заключается в "логические скобки" begin/end. Для функции необходимо в коде присвоить переменной с именем функции или специальной зарезервированной переменной Result (предпочтительно) возвращаемое функцией значение.

Примеры:

procedureИмя_процедуры(параметры); begin Код процедуры; end; function Имя_функции(параметры): тип_результ; begin Код функции; Result:=результат; end;

 

Описанная таким образом подпрограмма должна быть размещена в основной программе до первого её вызова. Иначе при компиляции получите извещение о том, что "неизвестный идентификатор..." Следить за этим не всегда удобно. Есть выход - разместить только заголовок подпрограммы там, где размещают описания всех данных программы.

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

Параметры в описании подпрограммы могут и отсутствовать, тогда она оперирует данными прямо из основной программы.

Теперь нужно ввести понятие локальных данных. Это данные - переменные, константы, подпрограммы, которые используются и существуют только в момент вызова данной подпрограммы. Они так же должны быть описаны в этой подпрограмме. Место их описания - между заголовком и началом логического блока - ключевым словом begin. Имена локальных данных могут совпадать с именами глобальных. В этом случае используется локальная переменная, причём её изменение не скажется на глобальной с тем же именем.

Совершенно аналогично локальным типам, переменным, константам могут быть введены и локальные процедуры и функции, которые могут быть описаны и использованы только внутри данной подпрограммы.

Теперь пример. Напишем программу суммирования двух чисел. Она будет состоять из Формы, на которой будет кнопка (компонент Button), по нажатию на которую будет выполняться наша подпрограмма, и двух строк ввода (компоненты Edit), куда будем вводить операнды. Начнём с процедуры.

Var Form1: TForm1; A, B, Summa: Integer; procedure Sum(A, B: Integer);   implementation   {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin A:=StrToInt(Edit1.Text); B:=StrToInt(Edit2.Text); Sum(A, B); Caption:=IntToStr(Summa); end; procedure Sum(A, B: Integer); begin Summa:=A+B; end;

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

Теперь сделаем то же самое с помощью функции.

Var Form1: TForm1;

A, B, Summa: Integer;

function Sum(A, B: Integer): Integer;

Implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

Begin

A:=StrToInt(Edit1.Text); B:=StrToInt(Edit2.Text);

Summa:=Sum(A, B); // На мой взгляд, сейчас более понятно, откуда что берётся

Caption:=IntToStr(Summa);

end;

function Sum(A, B: Integer): Integer;

Begin

Result:=A+B;

end;

 

Есть особенности в использовании в качестве параметров больших по объёму структур данных, например, массивов, состоящих из нескольких тысяч (и больше) элементов. При передаче в подпрограмму данных большого объёма могут быть большие расходы ресурсов и времени системы. Поэтому используется передача не самих значений элементов (передача "по значению", как в предыдущих примерах), а ссылки на имя переменной или константы (передача "по имени"). Достигается это вставкой перед теми параметрами, которые мы хотим передать по имени, ключевого слова var.

function Sum(A, B: Integer; var Arr: array[1..1000000] of Integer): Integer;

 

Если взглянуть на описание нашей подпрограммы и описание обработчика нажатия кнопки (это тоже подпрограмма!), который был создан Delphi, то видим, что перед именем обработчика (Button1Click) стоит TForm1. В Delphi точкой разделяется объект и его атрибуты (свойства и методы). Таким образом, Delphi создаёт Button1Click как метод объекта Form1. Причём, буква T перед объектом говорит о том, что Button1Click не просто метод объекта, а метод класса объекта. Не будем этим пока заморачиваться, а просто будем поступать также. Описав свою процедуру или функцию как метод класса TForm1, мы получаем возможность использовать в ней объекты класса без указания его имени, что гораздо удобнее. То есть, если мы используем в нашей подпрограмме какие-либо компоненты, размещённые на Форме (например, Button1), то мы пишем

Button1.Width:=100; //Ширина кнопки а не Form1.Button1.Width:=100;

Также появляется возможность использовать встроенные переменные, такие как параметр Sender. В каждом обработчике этот объект указывает на источник, то есть тот объект, который вызывает данную подпрограмму. Например, в нашей процедуре суммирования Sender = Button1. Проанализировав эту переменную, можно принять решение о тех или иных действиях.

Описав подпрограмму как метод класса, её описание мы должны поместить туда же, куда их помещает Delphi - в описание класса TForm1. Смотрите сами, где находится описание процедуры Button1Click. Для этого, поставив курсор внутрь подпрограммы Button1Click, нажмите CTRL+Shift и кнопку управления курсором "Вверх" или "Вниз" одновременно. Произойдёт переход к описанию подпрограммы (чтобы вернуться обратно, повторите это действие ещё раз). Ставьте описание своей подпрограммы рядом, с новой строки. Обратите внимание, что TForm1 уже не пишется.

Понятие рекурсии

Рекурсия - важное и мощное свойство процедур и функций в Delphi.

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

Рекурсивным называется объект, частично состоящий или определяемый с помощью самого себя. Факториал - это классический пример рекурсивного объекта. Факториал числа п - это произведение целых чисел от 1 до п. Обозначается факториал числа п так: n!.

Согласно определению

n! = 1 х 2 х 3 х ... х (п - 1) х п. Приведенное выражение можно переписать так:

n! = nх ((n - 1) х (n - 2) х ...х 3 х 2 х 1) = n х (n - 1)!

То есть, факториал числа п равен произведению числа п на факториал числа (п - 1). В свою очередь, факториал числа («-!) - это произведение числа (п - 1) на факториал числа (п - 2) и т. д.

Таким образом, если вычисление факториала п реализовать как функцию, то в теле этой функции будет инструкция вызова функции вычисления факториала числа (п - 1), т. е. функция будет вызывать сама себя. Такой способ вызова называется рекурсией, а функция, которая обращается сама к себе, называется рекурсивной функцией.

Листинг Рекурсивная функция вычисления факториала

function factorial(n: integer): integer;

Begin

if n <> 1

then factorials n * factorial(n-1)

// функция вызывает сама себя

else factorial := 1; // рекурсивный процесс закончен

end;

Обратите внимание, что функция вызывает сама себя только в том случае, если значение полученного параметра k не равно единице. Если значение параметра равно единице, то функция сама себя не вызывает, а возвращает значение, и рекурсивный процесс завершается.

На рисунке приведен вид диалогового окна программы, которая для вычисления факториала числа использует рекурсивную функцию factorial.

Окно программы вычисления факториала

Листинг Использование рекурсивной функции

Unit factor ;

Interface

uses Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms, Dialogs, StdCtrls;

Type

TForm1 = class(TForm)

Label1: TLabel;

Edit1: TEdit;

Button1: TButton;

Label2: TLabel;

procedure ButtonlClick(Sender: TObject) ;

Private

{ Private declarations } public

{ Public declarations } end;

Var Form1: TForm1;

Implementation

{$R *.DFM}

// рекурсивная функция

Function factorial(n: integer): integer;

Begin

if n > 1 then factorial := n * factorial(n-1) // функция вызывает сама себя

else factorial:= 1; // факториал 1 равен 1

end;

procedure TForml.ButtonlClick(Sender: TObject);

Var

k:integer; // число, факториал которого надо вычислить

f:integer; // значение факториала числа k

Begin

k := StrToInt(Edit1.Text);

f := factorial(k);

label2.caption:='Факториал числа '+Edit1.Text

+ ' равен '+IntToStr(f);

End; end.

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

 

 

 


Примеры работы программы вычисления факториала

 

Результат не соответствует ожидаемому, если он очень велик. Факториал числа 44 равен нулю! Произошло это потому, что факториал числа 44 настолько велик, что превысил максимальное значение для переменной типа integer, и, как говорят программисты, произошло переполнение с потерей значения.

Delphi может включить в исполняемую программу инструкции контроля диапазона значений переменных. Чтобы инструкции контроля были добавлены в программу, нужно во вкладке Compiler диалогового окна Project Options установить флажок Overflow checking(Контроль переполнения), который находится в группе Runtime errors (Ошибки времени выполнения).

 

 

Вкладка Compiler диалогового окна Project Options


 

Тема. Программирование графики. Вывод текстовой информации

Задание: Составить конспект.

План работы:

1 Ознакомиться с перечнем вопросов, подлежащих рассмотрению

2 Ознакомиться с представленным теоретическим материалам

3 Ответить на вопросы для самопроверки

4 Законспектировать ответы на вопросы, подлежащие рассмотрению, привести примеры

Включайте в конспект не только основные положения, но и обосновывающие их выводы, конкретные факты и примеры (без подробного описания).

5.Составляя конспект, записывайте отдельные слова сокращённо, выписывайте только ключевые слова, делайте ссылки на страницы конспектируемой литературы, применяйте условные обозначения.

6.Чтобы форма конспекта отражала его содержание, располагайте абзацы «ступеньками», подобно пунктам и подпунктам плана, применяйте разнообразные способы подчеркивания, используйте карандаши и ручки разного цвета.

Вопросы для самостоятельной работы

1 Компоненты, используемые при работе с гафикой и их свойства.

Дать краткую характеристику: холст, карандаш, кисть

2 Вывод текста.

Вопросы для самоконтроля:

1 Назовите основные компоненты, используемые при работе с гафикой

2 Из чего состоит холст

3 В чем отличия карандаша и кисти

 

Форма контроля: Оценка составленного конспекта.

 



Теоретический материал

Программа выводит графику на поверхность объекта (формы или компонента Image). Поверхности объекта соответствует свойство canvas. Для того чтобы вывести на поверхность объекта графический элемент (прямую линию, окружность, прямоугольник и т. д.), необходимо применить к свойству canvas этого объекта соответствующий метод. Например, инструкция Form1.Canvas.Rectangle (10,10,100,100) вычерчивает в окне программы прямоугольник.

Холст

Как было сказано ранее, поверхности, на которую программа может выводить графику, соответствует свойство Canvas. В свою очередь, свойство canvas — это объект типа TCanvas. Методы этого типа обеспечивают вывод графических примитивов (точек, линий, окружностей, прямоугольников и т. д.), а свойства позволяют задать характеристики выводимых графических примитивов: цвет, толщину и стиль линий; цвет и вид заполнения областей; характеристики шрифта при выводе текстовой информации.

Методы вывода графических примитивов рассматривают свойство Canvas как некоторый абстрактный холст, на котором они могут рисовать (canvas переводится как "поверхность", "холст для рисования"). Холст состоит из отдельных точек — пикселов. Положение пиксела характеризуется его горизонтальной (X) и вертикальной (Y) координатами. Левый верхний пиксел имеет координаты (0, 0). Координаты возрастают сверху вниз и слева направо (рис. 1). Значения координат правой нижней точки холста зависят от размера холста.

Рис. 1. Координаты точек холста

 

Размер холста можно получить, обратившись к свойствам Height и width области иллюстрации (image) или к свойствам формы:ClientHeight и Clientwidth.

Карандаш и кисть

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

Карандашу и кисти, используемым для вывода графики на холсте, соответствуют свойства Реn (карандаш) и Brush (кисть), которые представляют собой объекты типа треп и TBrush, соответственно. Значения свойств этих объектов определяют вид выводимых графических элементов.

Карандаш

Карандаш используется для вычерчивания точек, линий, контуров геометрических фигур: прямоугольников, окружностей, эллипсов, дуг и др. Вид линии, которую оставляет карандаш на поверхности холста, определяют свойства объекта треп, которые перечислены в табл. 1.

Таблица 1. Свойства объекта треп (карандаш)

Свойство Определяет
Color Цвет линии
Width Толщину линии
Style Вид линии
Mode Режим отображения

 

Свойство Color задает цвет линии, вычерчиваемой карандашом. В табл. 2 перечислены именованные константы (тип TCoior), которые можно использовать в качестве значения свойства color.

Таблица 2. Значение свойства Color определяет цвет линии

Константа Цвет Константа Цвет
clBlack Черный clSilver Серебристый
clMaroon Каштановый clRed Красный
clGreen Зеленый clLime Салатный
clOlive Оливковый clBlue Синий
clNavy Темно-синий clFuchsia Ярко-розовый
clPurple Розовый clAqua Бирюзовый
clTeal Зелено-голубой clWhite Белый
clGray Серый    

 

Свойство width задает толщину линии (в пикселах). Например, инструкция Canvas. Pen. width: =2 устанавливает толщину линии в 2 пиксела.

Свойство style определяет вид (стиль) линии, которая может быть непрерывной или прерывистой, состоящей из штрихов различной длины. В табл. 3 перечислены именованные константы, позволяющие задать стиль линии. Толщина пунктирной линии не может быть больше 1. Если значение свойства Pen.width больше единицы, то пунктирная линия будет выведена как сплошная.

 

Таблица 3. Значение свойства Реn. туре определяет вид линии

Константа Вид линии
psSolid Сплошная линия
psDash Пунктирная линия, длинные штрихи
psDot Пунктирная линия, короткие штрихи
psDashDot Пунктирная линия, чередование длинного и короткого штрихов
psDashDotDot Пунктирная линия, чередование одного длинного и двух коротких штрихов
psClear Линия не отображается (используется, если не надо изображать границу области, например, прямоугольника)

 

Свойство Mode определяет, как будет формироваться цвет точек линии в зависимости от цвета точек холста, через которые эта линия прочерчивается. По умолчанию вся линия вычерчивается цветом, определяемым значением свойства Pen.Color.

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

В табл. 4 перечислены некоторые константы, которые можно использовать в качестве значения свойства Pen.Mode.

Таблица 4 Значение свойства Реп. Mode влияет на цвет линии

Константа Цвет линии
pmBlack Черный, не зависит от значения свойства Pen. Color
pmWhite Белый, не зависит от значения свойства Pen. Color
pmCopy Цвет линии определяется значением свойства Pen . Color
pmNotCopy Цвет линии является инверсным по отношению к значению свойства Pen. Color
pmNot Цвет точки линии определяется как инверсный по отношению к цвету точки холста, в которую выводится точка линии

 

Кисть

Кисть (canvas.Brush) используется методами, обеспечивающими вычерчивание замкнутых областей, например геометрических фигур, для заливки (закрашивания) этих областей. Кисть, как объект, обладает двумя свойствами, перечисленными в табл.5.

 

Таблица 5. Свойства объекта TBrush (кисть)

Свойство Определяет
Color Style Цвет закрашивания замкнутой области Стиль (тип) заполнения области

Область внутри контура может быть закрашена или заштрихована. В первом случае область полностью перекрывает фон, а во втором — сквозь незаштрихованные участки области будет виден фон.

В качестве значения свойства Color можно использовать любую из констант типа TColor (см. список констант для свойства Pen.color в табл. 2).

Константы, позволяющие задать стиль заполнения области, приведены в табл. 6.

 

Таблица 6. Значения свойства Brush, style определяют тип закрашивания

Константа Тип заполнения (заливки) области
bsSolid Сплошная заливка
bsClear Область не закрашивается
bsHorizontal Горизонтальная штриховка
bsVertical Вертикальная штриховка
bsFDiagonal Диагональная штриховка с наклоном линий вперед
bsBDiagonal Диагональная штриховка с наклоном линий назад
bsCross Горизонтально-вертикальная штриховка, в клетку
bsDiagCross Диагональная штриховка, в клетку

 

В качестве примера в листинге 1 приведена программа Стили заполнения областей, которая в окно (рис. 2) выводит восемь прямоугольников, закрашенных черным цветом с использованием разных стилей.

 

Рис. 2. Окно программы Стили заполнения областей

Листинг 1. Стили заполнения областей

Unit brustyle_; interface

Uses Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms, Dialogs, ExtCtrls;

Type

TForm1 = class(TForm)

procedure FormPaint(Sender: TObject);

Private

{ Private declarations}

Public

{ Public declarations )

end;

var Form1: TForm1;

Implementation

{$R *.DFM}

// перерисовка формы

procedure TForm1.FormPaint(Sender: TObject);

Const

bsName: array[1..8] of string =('bsSolid', 'bsClear', 'bsHorizontal', 'bsVertical', 'bsFDiagonal', 'bsBDiagonal', 'bsCross','bsDiagCross');

var x,y: integer  // координаты левого верхнего угла прямоугольника

w,h: integer; // ширина и высота прямоугольника

bs: TBrushStyle;// стиль заполнения области

k: integer; // номер стиля заполнения

i,j: integer;

Begin

w:=40; h:=40; // размер области(прямоугольника)

у:=20;

for i:=l to 2 do begin х:=10;

for j:=1 to 4 do begin k:=j+(i-1)*4; // номер стиля заполнения

Case k of

1: bs = bsSolid;

2: bs = bsClear;

3: bs = bsHorizontal;

4: bs = bsVertical;

5: bs = bsFDiagonal;

6: bs = bsBDiagonal;

7: bs = bsCross;

8: bs = bsDiagCross; end;

// вывод прямоугольника

Canvas.Brush.Color := clGreen;

// цвет закрашивания — зеленый

Canvas.Brush.Style := bs;

// стиль закрашивания

Canvas . Rectangle (x, y, x+w, y-t-h) ;

// вывод названия стиля

Canvas.Brush.Style := bsClear;

Canvas.TextOut(x, y-15, bsName[k]);

// вывод названия стиля

x := x+w+30;

end ;

у := y+h+30;

End;end;end.

 

Вывод текста

Для вывода текста на поверхность графического объекта используется метод TextOut. Инструкция вызова метода TextOut в общем виде выглядит следующим образом:

Объект.Canvas.TextOut(x, у, Текст) где:

объект — имя объекта, на поверхность которого выводится текст;

х, у — координаты точки графической поверхности, от которой выполняется вывод текста (рис. 10.3);

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

Рис. 3. Координаты области вывода текста

 

Шрифт, который используется для вывода текста, определяется значением свойства Font соответствующего объекта canvas. Свойство Font представляет собой объект типа TFont. В табл. 7 перечислены свойства объекта TFont, позволяющие задать характеристики шрифта, используемого методами TextOut и TextRect для вывода текста.

Таблица 7. Свойства объекта TFont

Свойство Определяет
Name   Size   Style Используемый шрифт. В качестве значения следует использовать название шрифта, например Arial Размер шрифта в пунктах (points). Пункт— это единица измерения размера шрифта, используемая в полиграфии. Один пункт равен 1/72 дюйма Стиль начертания символов. Может быть: нормальным, полужирным, курсивным, подчеркнутым, перечеркнутым. Стиль задается при помощи следующих констант: fsBold (полужирный), fsltalic (курсив), f sUnderline (подчеркнутый), f sStrikeOut (перечеркнутый).
style     Color Свойство style является множеством, что позволяет комбинировать необходимые стили. Например, инструкция программы, устанавливающая стиль "полужирный курсив", выглядит так: Объект. Canvas . Font : = [fsBold, fs Italic] Цвет символов. В качестве значения можно использовать константу типа Tcolor

Внимание!

Область вывода текста закрашивается текущим цветом кисти. Поэтому перед выводом текста свойству Brush.Color нужно присвоить значение bsClear или задать цвет кисти, совпадающий с цветом поверхности, на которую выводится текст.

Следующий фрагмент программы демонстрирует использование функции Textout для вывода текста на поверхность формы:

with Form1.Canvas do begin

// установить характеристики шрифта

Font.Name := 'Tahoma';

Font.Size := 20;

Font.Style := [fsltalic, fsBold] ;

Brush.Style := bsClear; // область вывода текста не закраши-

TextOut(0, 10, 'Borland Delphi 7');

end;

 

После вывода текста методом Textout указатель вывода (карандаш) перемещается в правый верхний угол области вывода текста.

Иногда требуется вывести какой-либо текст после сообщения, длина которого во время разработки программы неизвестна. Например, это может быть слово "руб." после значения числа, записанного прописью. В этом случае необходимо знать координаты правой границы уже выведенного текста. Координаты правой границы текста, выведенного методом Textout, можно получить, обратившись к свойству PenPos.

Следующий фрагмент программы демонстрирует возможность вывода строки текста при помощи двух инструкций Textout.

with Form1.Canvas do begin

TextOut(0, 10, 'Borland ') ;

TextOut(PenPos.X, PenPos.Y, 'Delphi 7');

end;

 


Поделиться:



Последнее изменение этой страницы: 2019-04-11; Просмотров: 299; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.521 с.)
Главная | Случайная страница | Обратная связь