Odd — Функция Delphi


Функция Odd

Функция Odd в Паскале определяет, является ли число чётным или нечётным. Синтаксис:

Функция Odd возвращает TRUE, если число является НЕчётным. Иначе возвращает FALSE.

Как определить чётное число в Паскале

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

if not Odd(x) then WriteLn(‘x — чётное число ‘);

Как определить нечётное число в Паскале

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

if Odd(x) then WriteLn(‘x — нечётное число ‘);

Пример программы, где используется функция Odd:

odd в Delphi

28.10.2009, 11:24

Что лучше брать Delphi XE2, Delphi XE, Delphi 7?
Привет форумчане! У меня вопрос: что лучше брать Delphi XE2, Delphi XE, Delphi 7? Как вообще.

Четные записать в файл even.txt, нечетные записать в файл odd.txt
Тема: Файлы. Составить программу,которая считает с клавиатуры числа и четные записывает в файл.

Какие отличия Delphi 5, Delphi 6 и Delphi 7
Кто-нибудь юзал Delphi 6? Если да, то напишите, плиз, его отличия от 5-ой версии (плюсы и минусы).

Odd tag in Exec! Odd tag in Cleanup! Odd tag in CreateGroup! UnityEditor.DockArea:OnGUI()
Здравствуйте, столкнулся с такими ошибками Odd tag in Exec! Odd tag in Cleanup! Odd tag in.

Можно ли сравнить if odd(q[i,j])>odd(w[i,j]) then
В задании сказанно сравнить все нечетные числа матриц и вывести большую из нечетных меньше 100 на.

Odd — Функция Delphi

От Delphi 4 к Delphi 5
Перечислимые типы.

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

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

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

typeName = (val1. valn);

где typeName имя перечислимого типа, val — идентификаторы.

Например, следующее объявление:

Suite = (Club, Diamond, Heart, Spade);

определяет перечислимый тип, названный Иск, его возможные величины являются Клуб, Алмаз, Сердце и Лопата. К сожалению, в Object Pascal нельзя использовать кириллицу.

Когда вы объявляете перечислимый тип, вы определяете, что каждое значение val является константой типа typeName . Если идентификаторы val используются для другой цели в пределах той же области, то происходят конфликты присваивания имен. Например, вы объявляете следующий перечислимый тип:

TSound = (Click, Clack, Clock);

К сожалению, выражение Click является также именем метода определенного для TControl библиотеки Delphi VCL .

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


procedure TForm1.DBGrid1Enter(Sender: TObject);

var Thing: TSound;

Вы получите ошибку компиляции; компилятор интерпретирует Click в пределах области процедуры как ссылку на метод, щелчок по форме. Вы можете работать по-другому, квалифицируя идентификатор таким образом, что если TSound объявляется в MyUnit, вы должны использовать:

Лучшим решением, тем не менее, является выбор имен с некоторым изменением, например:

TSound = (tsClick, tsClack, tsClock);

TMyColor = (mcRed, mcBlue, mcGreen, mcYellow, mcOrange);

Answer = (ansYes, ansNo, ansMaybe);

Т.е. вы сохраняете понятный смысл имени, но не вступаете в конфликт с системой.

Вы можете использовать значения (val1. valn) при объявлении переменных, например:


var MyCard: (Club, Diamond, Heart, Spade);

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


var Card1: (Club, Diamond, Heart, Spade);

var Card2: (Club, Diamond, Heart, Spade);

генерирует ошибку компиляции. Но объявление


var Card1, Card2: (Club, Diamond, Heart, Spade);

ошибку компиляции не дает.

Или можно объявить таким образом:


type Suit = (Club, Diamond, Heart, Spade);

Соответствие между значениями перечисляемого типа и порядковыми номерами этих значений устанавливается порядком перечисления: первое значение в списке получает порядковый номер 0, второе значение — порядковый номер 1 и т.д.

Максимальное число констант перечислимого типа составляет 65536 значений, поэтому фактически перечислимый тип задает некоторое подмножество целого типа Word и может рассматриваться как компактное объявление сразу группы целочисленных констант со значениями 0, 1 и т.д.

Пусть, например, в программе должна быть переменная Mode, в которой зафиксирован один из возможных режимов работы приложения: чтение данных, их редактирование, запись данных. Можно, конечно, определить переменной Mode тип Word и присваивать этой переменной в нужные моменты времени одно из трех условных чисел: 0 — режим чтения, 1 — режим редактирования, 2 — режим записи. Тогда программа будет содержать операторы вида


if (Mode = 1) then .

Через некоторое время уже забудется, что означает значение Mode, равное 1, и разбираться в таком коде будет очень сложно. А можно поступить иначе: определить переменную Mode как переменную перечислимого типа и обозначить ее возможные значения как mRead, mEdit, mWrite . Тогда приведенный выше оператор изменится следующим образом:


if (Mode = mEdit) then .

Конечно, такой оператор понятнее, чем предыдущий.

Переменная перечислимого типа может определяться предложением вида:


var Mode : (mRead, mEdit, mWrite);

Если переменная определена так, то ей можно присваивать указанные значения, можно проверять ее величину, сравнивая с возможными значениями. Кроме того, надо учитывать, что перечислимые типы относятся к целым порядковым типам и к ним применимы любые операции сравнения (>, = ( , . );

Тогда можно ввести в программе несколько переменных этого типа. Например:

type TMode : (mRead, mEdit, mWrite);

var Mode1, Mode2 : TMode;

Для работы с перечислимыми типами имеется несколько системных подпрограмм, описанных в системном модуле Delphi.

Подпрограмма Ord возвращает число, которое указывает на порядковый номер значения параметра среди значений типа данных.

procedure TForm1.Button1Click(Sender: TObject);

S := ‘Blue имеет порядковую величину ‘ + IntToStr(Ord(BLUE)) + #13#10;

S := S + ‘Код ASCII для «c» — ‘ + IntToStr(Ord(‘c’)) + ‘ decimal’;

MessageDlg(S, mtInformation, [mbOk], 0);

На рисунке 1 представлен результат работы подпрограммы Ord.

procedure Dec(var X[; N: Longint]);

Процедура Dec уменьшает переданную в качестве параметра переменную на единицу или на значение необязательного второго параметра, т.е. уменьшает на единицу или на число N переменную.


X
— переменная перечислимого типа (включая Int64 ), или указатель, если позволен расширенный синтаксис.


N
— выражение целого типа.


procedure TForm1.Button1Click(Sender: TObject);

procedure Inc(var X [ ; N: Longint ] );

Процедура Inc увеличивает переданную в качестве параметра переменную на единицу или на заданное значение.


X
— переменная перечислимого типа (включая Int64 ), или указатель, если позволен расширенный синтаксис.


N
— выражение целого типа.

Функция Odd возвращает True, если параметр нечетное число.


function Odd(X: Longint): Boolean;


procedure TForm1.Button1Click(Sender: TObject);

Canvas.TextOut(10, 10, ‘5 нечетное.’)

Canvas.TextOut(10, 10, ‘Четное!’);

Функция Pred возвращает значение, которое в определяемой типом данных последовательности стоит перед значением параметра; предшественник.

X — выражение порядкового типа (включая Int64). Результат того же самого типа как предшественник X.

Succ возвращает значение, преемника.

X — выражение порядкового типа (включая Int64). Результат того же самого типа как X — преемник X. Не используйте Succ в процедурах записи.

Запишите следующий программный код, для проверки этой функции:

procedure TForm1.Button1Click(Sender: TObject);

S := ‘Предшественник для числа 5, ‘ + IntToStr(Pred(5)) + #13#10;

S := S + ‘Преемник числа 10, ‘ + IntToStr(Succ(10)) + #13#10;

if Succ(RED) = BLUE then

S := S + ‘Красный цвет предшествненник синего.’;

MessageDlg(S, mtInformation, [mbOk], 0);

На рисунке 2 показано выдаваемое сообщение по результатам работы этой программы.

Low возвращает самое низкое значение в диапазоне перечислимого типа, переданного как параметр.

High возвращает самое высокое значение в диапазоне перечислимого типа данных.

Рассмотрим пример, использующий функции Low и High.

function Sum( var X: array of Double): Double;

массива всегда нулевой.>

for I := 0 to High(X) do S := S + X[I];

procedure TForm1.Button1Click(Sender: TObject);

List1: array[0..3] of Double;

List2: array[5..17] of Double;

S, TempStr: string;

for X := Low(List1) to High(List1) do

for X := Low(List2) to High(List2) do

List2[X] := X * 0.0123;

S := ‘Sum of List1: ‘ + S + #13#10;

S := S + ‘Sum of List2: ‘;

MessageDlg(S, mtInformation, [mbOk], 0);

Результат работы программы показан на рисунке 3.


Ограниченные типы или тип-диапазон.

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

где определено минимальное и максимальное значение типа-диапазона.

Например, если вы объявляете перечисленный тип


type TColors = (Red, Blue, Green, Yellow, Orange, Purple, White, Black);


type TMyColors = Green..White;

Здесь TMyColors включает величины: зеленый, желтый, апельсиновый, пурпурный и белый.

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

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

В этих примерах переменная Alphabet может принимать только символы латинских букв в нижнем регистре, переменная Column принимает значения только целых чисел в диапазоне 1 — 12 (это могут быть, например, номера месяцев), переменная Day также принимает значения только целых чисел, но в диапазоне 1 — 31.

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

левое значение границы диапазона не должно превышать его правую границу;

два символа > рассматриваются как один символ, поэтому между ними недопустимы пробелы.

Тип-диапазон наследует все свойства своего базового типа, но с ограничениями, связанными с его меньшей емкостью. Так, по вполне понятным причинам процедуры Ord(X) и Pred(X) не всегда выполнимы.

Рассмотрим пример с использованием процедуры Inc :


type Percentile = 0..99;

var I: Percentile;

Для данного примера компилятор выдает ошибку, так как величина 100 не входит в диапазон объявленного ограниченного типа. Как вы помните, процедура Inc увеличивает переданную в качестве параметра переменную на единицу или на заданное значение.

В стандартную библиотеку Object Pascal введены две функции, поддерживающие работу с типами-диапазонами: High(X) и Low(X).

Ограниченные типы могут использоваться, например, для объявления размеров массивов, но и самостоятельно. В компиляторе Object Pascal имеется опция, позволяющая включить проверку диапазона при присваивании значения переменной ограниченного типа. Ее значение вы можете включить в том месте вашей программы, где вы хотите начать проверку диапазона, и выключить проверку, где захотите, опцией <$R->. Можно также включить опцию проверки в окне Project Options на странице Compiler . Это надежнее, так как опция работает только при возникновении ошибок диапазона, очевидных для компилятора, а установка опции проверки диапазона в окне Project Options действует в процессе выполнения (в режиме отладки) и при попытке присвоить переменной ограниченного типа значение, выходящее за пределы заданного диапазона, генерирует сообщение «Range check error «.

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

Этот компонент предназначен для оформления вашего приложения. Bevel позволяет выделять группы элементов, отделять их друг от друга.


type TBevelShape = (bsBox, bsFrame, bsTopLine, bsBottomLine, bsLeftLine, bsRightLine, bsSpacer);

property Shape: TBevelShape;

определяет контур компонента.


type TBevelStyle = (bsLowered, bsRaised);

property Style: TBevelStyle;

определяет стиль компонента (вдавленный или выпуклый). На рисунке 4 показаны свойства компонента Bevel.

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

Пользоваться этим компонентом достаточно просто. Поместите ScrollBox на форму и проведите размещение на управляющем элементе любых компонентов. Если очередной компонент выйдет за пределы рабочей области контейнера, то появятся полосы прокрутки (рисунок 5).


property BorderStyle: TBorderStyle;
определяет:

если BorderStyle = bsNone, то компонент не имеет видимой границы обрамления;

если BorderStyle = BsSingle, то компонент имеет видимую границу обрамления.

Рассмотрим пример создания мультимедиа-приложения, в котором используем свойства компонента Bevel.

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

Используя свой компьютер, вы можете получать качественное воспроизведение звука на внешних HiFi-колонках или встроенных в монитор, а также видеть прекрасную картинку, просматривая фильм.

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

Музыкальные произведения, звуковые эффекты и записи речевой информации хранятся на носителях информации в файлах специальных форматов, среди которых чаще всего применяется формат WAV (от слова wave — волна). Для хранения видеоинформации чаще всего используются файлы с расширением AVI ( Audio-Video-Information ). Обычно аудио- и видео-файлы очень большие по объему, поэтому они размещаются на лазерных дисках.

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

Для создания мультимедиа-приложения используется компонент Delphi, имя которого MediaPlayer со страницы System палитры компонентов. Внешне компонент выглядит как панель управления магнитофона, видеомагнитофона или музыкального центра с девятью кнопками.

  1. Запустите Delphi.
  2. Используя свойство Caption, для компонента Form1 введите заголовок «CD-проигрыватель».
  3. Поместите на форму компонент Bevel. Установите в свойстве Style значение bsRaised.
  4. На компонент Bevel поместите Label, введите заголовок «Проигрыватель компакт-дисков». Выровняйте компоненты, чтобы Label1 был посередине Bevel1.
  5. Разместите на форме еще один компонент Bevel, оставьте без изменений установленное по умолчанию свойство Style равное bsLowered .
  6. Поместите на Bevel2 компонент MediaPlayer. Создайте мультимедиа приложение для проигрывания компакт-дисков.
  7. Для этого установите свойство Device Type в значение dtCDAudio, а свойству AutoOpen установите значение True.
  8. Используя комплексное свойство VisibleButtons, установите значения btStep = False, btBack = False, btRecord = False. Благодаря этим действиям с панели будут убраны все ненужные кнопки (рисунок 6) .
  9. После запуска приложения на компиляцию можете вставить компакт-диск в устройство CD-ROM и щелчком по кнопке Play осуществить воспроизведение ваших любимых мелодий.
  10. Можете добавить в свое приложение компонент Image и внедрить картинку. Размещение компонентов можно сделать как на рисунке 7, используя при этом всплывающее меню, вызываемое щелчком правой кнопкой мыши.
  1. Марко Канту. Delphi 2 для Windows 95/NT. Москва. ООО «Малип». 1997г.
  2. Джон Матчо. Дэвид Р. Фолкнер. Delphi. Москва. БИНОМ. 1995г.
  3. Эндрю Возневич. Delphi. Освой самостоятельно. Москва. Восточная книжная компания. 1996г.
  4. К. Сурков, Д. Сурков, А. Вальвачев. Программирование в среде Delphi 2.0. Минск. 1997 г. ООО «Попурри».
  5. В.В.Фаронов. Delphi 5. Учебный курс. Москва. Издательство Нолидж. 2000 г.
  6. А. Я. Архангельский. Программирование в Delphi 5. Москва. ЗАО «Издательство Бином». 2000г.


Владимир Скуратов

Odd — Функция Delphi

Контакты: о проблемах с регистрацией, почтой и по другим вопросам пишите сюда — alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail.

Форум программистов > Технологии > Помощь студентам
четные и нечетные (delphi 7)
Регистрация
Поиск по форуму
Расширенный поиск
К странице.

Здесь нужно купить рекламу за 25 тыс руб в месяц! ) пишите сюда — alarforum@yandex.ru

Привет всем! Помогите пожалуйста с решением задач на делфи и если можно объясните вкратце, буду очень признателен!

1. Дана числовая последовательность. Найти количество чисел, имеющих четные порядковые номера и являющиеся нечетными.

2. Дано действительное число a, натуральное число n. Вычислить:
a(a+1). (a+n+1).

19.11.2009, 00:45 #1
19.11.2009, 07:58 #2
19.11.2009, 14:21 #3

Что касается 1. Результат выводится единица. Можете показать полный код программы или сказать где у меня ошибка.

delphi directoryexists function odd behaviour for network mapped units

In delphi XE, when I call SysUtils DirectoryExists function with the following input

where Y is a network mapped unit, it correctly returns false because blabla doesnt exist.

But when I call with the following input

it returns true.

Documentation is poor, and I’ve not found anywhere on the internets people with the same problem

maybe someone here already had this problem, or know what is happening?

2 Answers 2

It seems a bug in the implementation of the DirectoryExists function.

This is the relevant code of this function

As you see if the GetFileAttributes function call fails, the result of the GetLastError method is compared against a set of possible values. but in your case passing a invalid path will return a ERROR_BAD_PATHNAME (161) code, so the function returns True.

The bug is still there in XE8 (and probably in other versions too). As pointed out above by RRUZ it lies in the SysUtils implementations of BOTH DirectoryExists() and TDirectory.Exists().

The problem is with that long list of «checks» that assumes that they are the only valid reasons that INVALID_FILE_ATTRIBUTES may have been returned in the context of «existence» of the folder. But the reason we call these routines is nearly always so we can check whether we can actually use the folder we are asking about. Having INVALID_FILE_ATTRIBUTES almost always means that we cannot. Either way, the tests presently being done do not produce sensible results. This fact alone makes it a totally redundant exercise, as it allows certain other fault codes to slip through the net and set the end result to TRUE when it should not be. Whilst I can only imagine that there was originally some method to this madness, along the lines of «oh, it exists but might be invalid», it falls foul of the inherent truth that the future may bring many more codes generated by many as yet unknown file systems and/or hardware: so the bottom line is that for 99.9% of usage, getting INVALID_FILE_ATTRIBUTES should mean folder not usable, and it is therefore illogical to allow TRUE to be returned once that has already been established.

Unfortunately we cannot know whether there is code that relies upon the present behavior to identify «existing paths with issues» — in which case the call to the routine as it presently exists would have to be preceded with a path-syntax validation check first: otherwise it would say that a path described with bad syntax «exists», which is a nonsense! You can’t redo the GetLastError afterwards, because the error will have already been cleared.

So the only cure is to wrap any Sysutils.DirectoryExists and (unfortunately also) TDirectory.Exists calls with code that eliminates the problem up-front. For example:

Either that, or you do a separate up-front validation of all the «bad» cases you know about and Embarcadero missed — i.e. play the same losing game as they did. Horrible, but there you go.

You could also modify the SysUtils library yourself, to add the missing cases, which you would have to redo with every release of Delphi and for every new case you encounter. It would probably be better if Embarcadero finally «bites the bullet» and finds a better solution for this problem. Perhaps by using another defaulted flag parameter that says «reject all invalid directories». I would further suggest that this defaults to TRUE.

odd в паскале

Delphi odd

Автор Ilja Kiselev задал вопрос в разделе Другие языки и технологии

Что в Паскале за функция Odd? и получил лучший ответ

Ответ от Андрей Злыгостев[новичек]
определяет является ли целое число нечетным. т. е. возвращает true для нечетных, false для четных.Это типа такое экстремальное ускорение с проверкой всего одного бита вместо использования тяжелой операции целочисленного деления ��

Odd — Функция Delphi

От Delphi 4 к Delphi 5
Перечислимые типы.

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

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

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

typeName = (val1. valn);

где typeName имя перечислимого типа, val — идентификаторы.

Например, следующее объявление:

Suite = (Club, Diamond, Heart, Spade);

определяет перечислимый тип, названный Иск, его возможные величины являются Клуб, Алмаз, Сердце и Лопата. К сожалению, в Object Pascal нельзя использовать кириллицу.

Когда вы объявляете перечислимый тип, вы определяете, что каждое значение val является константой типа typeName . Если идентификаторы val используются для другой цели в пределах той же области, то происходят конфликты присваивания имен. Например, вы объявляете следующий перечислимый тип:

TSound = (Click, Clack, Clock);

К сожалению, выражение Click является также именем метода определенного для TControl библиотеки Delphi VCL .

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


procedure TForm1.DBGrid1Enter(Sender: TObject);

var Thing: TSound;

Вы получите ошибку компиляции; компилятор интерпретирует Click в пределах области процедуры как ссылку на метод, щелчок по форме. Вы можете работать по-другому, квалифицируя идентификатор таким образом, что если TSound объявляется в MyUnit, вы должны использовать:

Лучшим решением, тем не менее, является выбор имен с некоторым изменением, например:

TSound = (tsClick, tsClack, tsClock);

TMyColor = (mcRed, mcBlue, mcGreen, mcYellow, mcOrange);

Answer = (ansYes, ansNo, ansMaybe);

Т.е. вы сохраняете понятный смысл имени, но не вступаете в конфликт с системой.

Вы можете использовать значения (val1. valn) при объявлении переменных, например:


var MyCard: (Club, Diamond, Heart, Spade);

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


var Card1: (Club, Diamond, Heart, Spade);

var Card2: (Club, Diamond, Heart, Spade);

генерирует ошибку компиляции. Но объявление


var Card1, Card2: (Club, Diamond, Heart, Spade);

ошибку компиляции не дает.

Или можно объявить таким образом:


type Suit = (Club, Diamond, Heart, Spade);

Соответствие между значениями перечисляемого типа и порядковыми номерами этих значений устанавливается порядком перечисления: первое значение в списке получает порядковый номер 0, второе значение — порядковый номер 1 и т.д.

Максимальное число констант перечислимого типа составляет 65536 значений, поэтому фактически перечислимый тип задает некоторое подмножество целого типа Word и может рассматриваться как компактное объявление сразу группы целочисленных констант со значениями 0, 1 и т.д.

Пусть, например, в программе должна быть переменная Mode, в которой зафиксирован один из возможных режимов работы приложения: чтение данных, их редактирование, запись данных. Можно, конечно, определить переменной Mode тип Word и присваивать этой переменной в нужные моменты времени одно из трех условных чисел: 0 — режим чтения, 1 — режим редактирования, 2 — режим записи. Тогда программа будет содержать операторы вида


if (Mode = 1) then .

Через некоторое время уже забудется, что означает значение Mode, равное 1, и разбираться в таком коде будет очень сложно. А можно поступить иначе: определить переменную Mode как переменную перечислимого типа и обозначить ее возможные значения как mRead, mEdit, mWrite . Тогда приведенный выше оператор изменится следующим образом:


if (Mode = mEdit) then .

Конечно, такой оператор понятнее, чем предыдущий.

Переменная перечислимого типа может определяться предложением вида:


var Mode : (mRead, mEdit, mWrite);

Если переменная определена так, то ей можно присваивать указанные значения, можно проверять ее величину, сравнивая с возможными значениями. Кроме того, надо учитывать, что перечислимые типы относятся к целым порядковым типам и к ним применимы любые операции сравнения (>, = ( , . );

Тогда можно ввести в программе несколько переменных этого типа. Например:

type TMode : (mRead, mEdit, mWrite);

var Mode1, Mode2 : TMode;

Для работы с перечислимыми типами имеется несколько системных подпрограмм, описанных в системном модуле Delphi.

Подпрограмма Ord возвращает число, которое указывает на порядковый номер значения параметра среди значений типа данных.

procedure TForm1.Button1Click(Sender: TObject);

S := ‘Blue имеет порядковую величину ‘ + IntToStr(Ord(BLUE)) + #13#10;

S := S + ‘Код ASCII для «c» — ‘ + IntToStr(Ord(‘c’)) + ‘ decimal’;

MessageDlg(S, mtInformation, [mbOk], 0);

На рисунке 1 представлен результат работы подпрограммы Ord.

procedure Dec(var X[; N: Longint]);

Процедура Dec уменьшает переданную в качестве параметра переменную на единицу или на значение необязательного второго параметра, т.е. уменьшает на единицу или на число N переменную.


X
— переменная перечислимого типа (включая Int64 ), или указатель, если позволен расширенный синтаксис.


N
— выражение целого типа.


procedure TForm1.Button1Click(Sender: TObject);

procedure Inc(var X [ ; N: Longint ] );

Процедура Inc увеличивает переданную в качестве параметра переменную на единицу или на заданное значение.


X
— переменная перечислимого типа (включая Int64 ), или указатель, если позволен расширенный синтаксис.


N
— выражение целого типа.

Функция Odd возвращает True, если параметр нечетное число.


function Odd(X: Longint): Boolean;


procedure TForm1.Button1Click(Sender: TObject);

Canvas.TextOut(10, 10, ‘5 нечетное.’)


Canvas.TextOut(10, 10, ‘Четное!’);

Функция Pred возвращает значение, которое в определяемой типом данных последовательности стоит перед значением параметра; предшественник.

X — выражение порядкового типа (включая Int64). Результат того же самого типа как предшественник X.

Succ возвращает значение, преемника.

X — выражение порядкового типа (включая Int64). Результат того же самого типа как X — преемник X. Не используйте Succ в процедурах записи.

Запишите следующий программный код, для проверки этой функции:

procedure TForm1.Button1Click(Sender: TObject);

S := ‘Предшественник для числа 5, ‘ + IntToStr(Pred(5)) + #13#10;

S := S + ‘Преемник числа 10, ‘ + IntToStr(Succ(10)) + #13#10;

if Succ(RED) = BLUE then

S := S + ‘Красный цвет предшествненник синего.’;

MessageDlg(S, mtInformation, [mbOk], 0);

На рисунке 2 показано выдаваемое сообщение по результатам работы этой программы.

Low возвращает самое низкое значение в диапазоне перечислимого типа, переданного как параметр.

High возвращает самое высокое значение в диапазоне перечислимого типа данных.

Рассмотрим пример, использующий функции Low и High.

function Sum( var X: array of Double): Double;

массива всегда нулевой.>

for I := 0 to High(X) do S := S + X[I];

procedure TForm1.Button1Click(Sender: TObject);

List1: array[0..3] of Double;

List2: array[5..17] of Double;

S, TempStr: string;

for X := Low(List1) to High(List1) do

for X := Low(List2) to High(List2) do

List2[X] := X * 0.0123;

S := ‘Sum of List1: ‘ + S + #13#10;

S := S + ‘Sum of List2: ‘;

MessageDlg(S, mtInformation, [mbOk], 0);

Результат работы программы показан на рисунке 3.


Ограниченные типы или тип-диапазон.

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

где определено минимальное и максимальное значение типа-диапазона.

Например, если вы объявляете перечисленный тип


type TColors = (Red, Blue, Green, Yellow, Orange, Purple, White, Black);


type TMyColors = Green..White;

Здесь TMyColors включает величины: зеленый, желтый, апельсиновый, пурпурный и белый.

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

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

В этих примерах переменная Alphabet может принимать только символы латинских букв в нижнем регистре, переменная Column принимает значения только целых чисел в диапазоне 1 — 12 (это могут быть, например, номера месяцев), переменная Day также принимает значения только целых чисел, но в диапазоне 1 — 31.

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

левое значение границы диапазона не должно превышать его правую границу;

два символа > рассматриваются как один символ, поэтому между ними недопустимы пробелы.

Тип-диапазон наследует все свойства своего базового типа, но с ограничениями, связанными с его меньшей емкостью. Так, по вполне понятным причинам процедуры Ord(X) и Pred(X) не всегда выполнимы.

Рассмотрим пример с использованием процедуры Inc :


type Percentile = 0..99;

var I: Percentile;

Для данного примера компилятор выдает ошибку, так как величина 100 не входит в диапазон объявленного ограниченного типа. Как вы помните, процедура Inc увеличивает переданную в качестве параметра переменную на единицу или на заданное значение.

В стандартную библиотеку Object Pascal введены две функции, поддерживающие работу с типами-диапазонами: High(X) и Low(X).

Ограниченные типы могут использоваться, например, для объявления размеров массивов, но и самостоятельно. В компиляторе Object Pascal имеется опция, позволяющая включить проверку диапазона при присваивании значения переменной ограниченного типа. Ее значение вы можете включить в том месте вашей программы, где вы хотите начать проверку диапазона, и выключить проверку, где захотите, опцией <$R->. Можно также включить опцию проверки в окне Project Options на странице Compiler . Это надежнее, так как опция работает только при возникновении ошибок диапазона, очевидных для компилятора, а установка опции проверки диапазона в окне Project Options действует в процессе выполнения (в режиме отладки) и при попытке присвоить переменной ограниченного типа значение, выходящее за пределы заданного диапазона, генерирует сообщение «Range check error «.

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

Этот компонент предназначен для оформления вашего приложения. Bevel позволяет выделять группы элементов, отделять их друг от друга.


type TBevelShape = (bsBox, bsFrame, bsTopLine, bsBottomLine, bsLeftLine, bsRightLine, bsSpacer);

property Shape: TBevelShape;

определяет контур компонента.


type TBevelStyle = (bsLowered, bsRaised);

property Style: TBevelStyle;

определяет стиль компонента (вдавленный или выпуклый). На рисунке 4 показаны свойства компонента Bevel.

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

Пользоваться этим компонентом достаточно просто. Поместите ScrollBox на форму и проведите размещение на управляющем элементе любых компонентов. Если очередной компонент выйдет за пределы рабочей области контейнера, то появятся полосы прокрутки (рисунок 5).


property BorderStyle: TBorderStyle;
определяет:

если BorderStyle = bsNone, то компонент не имеет видимой границы обрамления;

если BorderStyle = BsSingle, то компонент имеет видимую границу обрамления.

Рассмотрим пример создания мультимедиа-приложения, в котором используем свойства компонента Bevel.

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

Используя свой компьютер, вы можете получать качественное воспроизведение звука на внешних HiFi-колонках или встроенных в монитор, а также видеть прекрасную картинку, просматривая фильм.

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

Музыкальные произведения, звуковые эффекты и записи речевой информации хранятся на носителях информации в файлах специальных форматов, среди которых чаще всего применяется формат WAV (от слова wave — волна). Для хранения видеоинформации чаще всего используются файлы с расширением AVI ( Audio-Video-Information ). Обычно аудио- и видео-файлы очень большие по объему, поэтому они размещаются на лазерных дисках.

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

Для создания мультимедиа-приложения используется компонент Delphi, имя которого MediaPlayer со страницы System палитры компонентов. Внешне компонент выглядит как панель управления магнитофона, видеомагнитофона или музыкального центра с девятью кнопками.

  1. Запустите Delphi.
  2. Используя свойство Caption, для компонента Form1 введите заголовок «CD-проигрыватель».
  3. Поместите на форму компонент Bevel. Установите в свойстве Style значение bsRaised.
  4. На компонент Bevel поместите Label, введите заголовок «Проигрыватель компакт-дисков». Выровняйте компоненты, чтобы Label1 был посередине Bevel1.
  5. Разместите на форме еще один компонент Bevel, оставьте без изменений установленное по умолчанию свойство Style равное bsLowered .
  6. Поместите на Bevel2 компонент MediaPlayer. Создайте мультимедиа приложение для проигрывания компакт-дисков.
  7. Для этого установите свойство Device Type в значение dtCDAudio, а свойству AutoOpen установите значение True.
  8. Используя комплексное свойство VisibleButtons, установите значения btStep = False, btBack = False, btRecord = False. Благодаря этим действиям с панели будут убраны все ненужные кнопки (рисунок 6) .
  9. После запуска приложения на компиляцию можете вставить компакт-диск в устройство CD-ROM и щелчком по кнопке Play осуществить воспроизведение ваших любимых мелодий.
  10. Можете добавить в свое приложение компонент Image и внедрить картинку. Размещение компонентов можно сделать как на рисунке 7, используя при этом всплывающее меню, вызываемое щелчком правой кнопкой мыши.
  1. Марко Канту. Delphi 2 для Windows 95/NT. Москва. ООО «Малип». 1997г.
  2. Джон Матчо. Дэвид Р. Фолкнер. Delphi. Москва. БИНОМ. 1995г.
  3. Эндрю Возневич. Delphi. Освой самостоятельно. Москва. Восточная книжная компания. 1996г.
  4. К. Сурков, Д. Сурков, А. Вальвачев. Программирование в среде Delphi 2.0. Минск. 1997 г. ООО «Попурри».
  5. В.В.Фаронов. Delphi 5. Учебный курс. Москва. Издательство Нолидж. 2000 г.
  6. А. Я. Архангельский. Программирование в Delphi 5. Москва. ЗАО «Издательство Бином». 2000г.


Владимир Скуратов

Odd — Функция Delphi

Изучив основные «кирпичики», из которых составляются программные инструкции, а именно — переменные и операторы, мы можем приступить к исследованию вопросов их эффективного расположения в теле программы. Для этих целей рассмотрим вопрос использования подпрограмм.

О подпрограммах в Object Pascal

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

Структура подпрограммы похожа на программу в миниатюре: она содержит заголовок, блок объявления переменных и блок инструкций. Из отличий можно выделить лишь невозможность подключать модули (блок uses), а так же ограничения на объявления типов данных: если локальные простые и даже составные типы в подпрограммах вполне допустимы, то более сложные типы — объекты, классы и интерфейсы, локальными быть не могут, а потому в подпрограммах их объявлять нельзя.

Использование подпрограммы состоит из 2 этапов: сначала подпрограмму описывают, а затем, уже в блоке инструкций программы, вызывают. Отметим, что в библиотеке Delphi имеется описание тысяч готовых подпрограмм, описывать которые, разумеется, уже не надо. А их вызовом мы уже неоднократно занимались — достаточно взглянуть на любой пример, где мы встречали инструкции, подобные таким:

write(‘Hello, world!’); readln;

Здесь и write, и readln — стандартные подпрограммы Object Pascal. Таким образом, с вызовом подпрограмм мы уже знакомы. Осталось узнать, как создавать собственные, или пользовательские, подпрограммы. Но прежде отметим, что все подпрограммы делятся на 2 лагеря: процедуры и функции. Мы уже использовали эти термины, и даже давали им описание, однако повторимся: процедуры — это такие подпрограммы, которые выполняют предназначенное действие и возвращают выполнение в точку вызова. Функции в целом аналогичны процедурам, за тем исключением, что они еще и возвращают результат своего выполнения. Результатом работы функции могут быть данные любого типа, включая объекты.

Вместе с тем, значение, возвращаемое функцией, можно проигнорировать, в таком случае она ничем не будет отличаться от процедуры. Разумеется, при этом функция все-таки должна выполнить какое-либо действие, сказывающееся на выполнении программы, иначе она потеряет всякий смысл. С другой стороны, процедуры могут возвращать значения через свои параметры — например, как это делает DecodeDate. Таким образом, различия между процедурами и функциями в современном программировании весьма призрачны.

Как процедурам, так и функциям могут передаваться данные для обработки. Делается это при помощи списка параметров. Список параметров в описании подпрограммы и список аргументов, указываемых при ее вызове должен совпадать. Иначе говоря, если в описании определено 2 параметра типа Integer, то, вызывая такую подпрограмму, в качестве аргументов так же следует указать именно 2 аргумента и именно типа Integer или совместимого (скажем, Word или Int64).

ПРИМЕЧАНИЕ
На самом деле, Object Pascal позволяет довольно гибко обращаться с аргументами, для чего имеются различные методы, включая «перегружаемые» функции, значения параметров по умолчанию и т.д. Тем не менее, в типичном случае, количество, тип, и порядок перечисления аргументов при объявлении и при вызове процедуры или функции, должны совпадать.

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

Процедуры

Итак, начнем исследование подпрограммы с процедур. Как уже было отмечено, процедуру надо описать. Описание процедуры состоит из заголовка и тела процедуры.

Заголовок состоит из ключевого слова procedure, за которым следует имя процедуры и, при необходимости, список параметров, заключенных в круглые скобки:

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

ПРИМЕЧАНИЕ
Вопросы локальных и глобальных переменных, и вообще видимости в программах, будет рассмотрен позже в этой главе.

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

procedure TriplePrint(str: string); var i: integer; begin for i := 1 to 3 do begin writeln(‘»‘+str+'»‘); end; // конец цикла for end; // конец процедуры TriplePrint

Здесь мы определили процедуру TriplePrint, которая будет трижды выводить переданную ей в качестве аргумента строку, заключенную в двойные кавычки. Как видно, данная процедура имеет все составные части: ключевое слово procedure, имя, список параметров (в данном случае он всего один — строковая переменная str), блок объявления собственных переменных (целочисленная переменная i), и собственное тело, состоящее из оператора цикла for.

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

Отметим так же, что рассмотренная нами процедура сама содержит вызов другой процедуры — writeln. Процедуры могут быть встроенными. Иначе говоря, объявление одной процедуры можно помещать в заголовочную часть другой. Например, наша процедура TriplePrint может иметь вспомогательную процедуру, которая будет «подготавливать» строку к выводу. Для этого перед объявлением переменной i, разместим объявление еще одной процедуры. Назовем ее PrepareStr:

procedure PrepareStr; begin str := ‘»‘+str+'»‘; end;

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

Таким образом, мы получаем две процедуры, одна из которых (TriplePrint) может использоваться во всей программе, а другая (PrepareStr) — только внутри процедуры TriplePrint. Чтобы преимущество использования процедур было очевидно, рассмотрим их на примере программы, которая будет использовать ее неоднократно, для чего обратимся к листингу 6.1 (см. так же пример в Demo\Part1\Procs).

Листинг 6.1. Использование процедур

program procs; <$APPTYPE CONSOLE>procedure TriplePrint(str: string); procedure PrepareStr; begin str := ‘»‘+str+'»‘; end; var i: integer; begin PrepareStr; for i := 1 to 3 do begin writeln(str); end; end; // конец процедуры TriplePrint begin // начало тела основной программы TriplePrint(‘Hello. ‘); // первый вызов TriplePrint TriplePrint(‘How are you. ‘); // 2-й вызов TriplePrint(‘Bye. ‘); // 3-й readln; end.

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

Функции

Подобно процедурам, описание функции состоит из заголовка и тела. Однако описание заголовка имеет 2 отличия: прежде всего, для функций используется ключевое слово function. Кроме того, поскольку функции всегда возвращают результат, завершается строка заголовка типом возвращаемого значения. Таким образом, для объявления функции мы получаем следующий синтаксис:

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

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

function cube(value: integer) : integer; result := value * value * value; >

Здесь определена функция, имеющая параметр value типа целого числа, которое она возводит в третью степень путем троекратного умножения, и результат присваивается специальной переменной result. Таким образом, чтобы в любом месте программы вычислить значение числа в 3-й степени, достаточно написать такое выражение:

В результате выполнения этого выражения переменной x будет присвоено значение 27. Данный пример иллюстрирует использование функций в классическом случае — для явного вычисления значения переменной. Однако функции могут использоваться в выражениях и напрямую. Например, можно поставить вызов функции cube в каком-либо месте арифметического выражения подобно обычной переменной:

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

procedure TriplePrint(str: string); function PrepareStr(s: string) : string; begin result := ‘»‘+s+'»‘; end; var i: integer; begin for i := 1 to 3 do begin writeln(PrepareStr(str)); // функция использована как переменная end; end;

Как уже отмечалось, помимо специальной переменной result, в функциях можно использовать другую автоматически объявляемую переменную, имя которой соответствует имени функции. Так, для функции cube имя переменной также будет cube:

function cube(value: integer) : integer; cube := value * value * value; >

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

Рекурсия

Таким образом мы подошли к теме рекурсии — вызову подпрограммы из самой себя. Это не является ошибкой, более того, целый ряд алгоритмов решить без рекурсии вообще было бы затруднительно.

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

function recfunc(x: integer) : integer begin dec(x); // функция декремента, уменьшает целое на 1 if x > 5 then x := recfunc(x); result := 0; // возвращаемое значение тут не используется end;

Здесь мы объявили функцию recfunc, принимающую один аргумент, и вызывающую саму себя до тех пор, пока значение этого аргумента больше 5. Хотя на первый взгляд может показаться, что такое поведение функции похоже на обычный цикл, на самом деле все работает несколько по-иному: если вы вызовите ее со значением 8, то она выдаст вам 3 сообщения в следующей последовательности: 5, 6, 7. Иначе говоря, функция вызывала саму себя до тех пор, пока значение x было больше 5, и собственно вывод сообщений начала 3-я по уровню получившейся вложенности функция, которая и вывела первое сообщение (в данном случае им стало 5, т.е. уменьшенное на единицу 6).

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

Листинг 6.2. Рекурсия с комментариями

program recurse; <$APPTYPE CONSOLE>function recfunc(x, depth: integer) : integer; begin dec(x); if x > 5 then begin write(‘Current recursion depth is: ‘); write(depth); write(‘, current x value is: ‘); writeln(x); inc(depth); depth:=recfunc(x, depth); end else writeln(‘End of recursive calls. ‘); write(‘Current recursion depth is: ‘); write(depth); write(‘, current x value is: ‘); writeln(x); dec(depth); result := depth; end; begin recfunc(8,0); readln; end.

Исходный код находится в Demo\Part1\Recurse, там же находится и исполняемый файл recurse.exe, результат работы которого вы можете увидеть на своем экране.

Использование параметров

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

procedure Circle (square: real; var radius, length: real);

Данная процедура принимает «на обработку» одно значение — площадь (square), а возвращает через свои параметры два — радиус (radius) и длину окружности (length). Практическая ее реализация может выглядеть таким образом:

procedure Circle (square: real; var radius, length: real); begin radius := sqrt(square / pi); // функция pi возвращает значение числа ? length := pi * radius * 2; end;

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

var r,l: real; . Circle(100,r,l);

После вызова функции Circle, переменные r и l получат значения радиуса и длины окружности. Остается их вывести при помощи writeln. Исходный код программы приведен в листинге 6.3.

Листинг 6.3. Процедура с параметрами

program params; <$APPTYPE CONSOLE>procedure Circle (square: real; var radius, length: real); begin //функция sqrt извлекает корень, а функция pi возвращает значение числа ? radius := sqrt(square / pi); length := pi * radius * 2; end; var r,l: real; begin Circle(100,r,l); writeln(r); writeln(l); readln; end.

Запустив такую программу, можно убедиться, что она работает и выводит верные результаты, однако вид у них получается довольно-таки неудобочитаемый, например, длина окружности будет представлена как «3,54490770181103E+0001». Чтобы сделать вывод более удобным для восприятия, нам понадобится функция FloatToStrF. С ее помощью мы можем определить вывод числа на свое усмотрение, например:

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

writeln(‘Radius is: ‘+FloatToStrF(r,ffFixed,12,8)); writeln(‘Length is: ‘+FloatToStrF(l,ffFixed,12,8));

Наконец, не помешало бы сделать программу более полезной, для чего предусмотрим возможность ввода значения площади круга пользователем. В этих целях нам понадобится еще одна переменная (назовем ее s) и выражение для считывания ввода. Не помешает так же приглашение, объясняющее пользователю, что надо делать. В итоге основной блок программы получит следующий вид:

. var s,r,l: real; begin write(‘Input square: ‘); readln(s); Circle(s,r,l); writeln(‘Radius is: ‘+FloatToStrF(r,ffFixed,12,8)); writeln(‘Length is: ‘+FloatToStrF(l,ffFixed,12,8)); readln; end.

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

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

function Circle(square: real; var radius, length: real) : boolean; begin result := false; if (square

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

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

if Circle(s,r,l) then begin // вывод end else // сообщить об ошибке

Результатом проделанной работы будет программа, приведенная в листинге 6.4. Она же находится в Demo\Part1\Params.

Листинг 6.4. Функция с параметрами

program params; <$APPTYPE CONSOLE>uses sysutils; //этот модуль соджержит функцию FloatToStrF function Circle(square: real; var radius, length: real) : boolean; begin result := false; if (square

Итак, при помощи ключевого слова var в списке параметров подпрограммы мы можем добиться использования передаваемых аргументов в том блоке, где был произведен вызов данной подпрограммы. В несколько другом аспекте используется ключевое слово const. Фактически, оно объявляет локальную константу, т.е. значение, которое нельзя изменять внутри данной процедуры или функции. Это бывает полезным в том случае, когда такое изменение недопустимо по логике программы и служит гарантией того, что такое значение не будет изменено.

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

function MyBetterFunc(val1: integer; const val2: integer = 2); begin result := val1*val2; end;

Обращение же к такой функции может иметь 2 варианта: с указанием только одного аргумента (для параметра val1), или же с указанием обоих:

x := MyBetterFunc(5); // получим 10 x := MyBetterFunc(5,4); // получим 20

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

Области видимости

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

program Project1; procedure Proc1; var a: integer; begin a := 5; //верно. Локальная переменная a здесь видна end; begin a := 10; //Ошибка! Объявленная в процедуре Proc1 переменнаая здесь не видна end.

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

program Project2; var a: integer; // глобальная переменная a procedure Proc1; begin a := 5; // верно b := 10; // Ошибка! Переменая b на этот момент еще не объявлена end; var b: integer; // глобальная переменная b begin a := 10; // верно b := 5; // тоже верно. Здесь видны все г var a: integer; // глобальная переменная end.

Теперь рассмотрим такой вариант, когда у нас имеются 2 переменных с одним и тем же именем. Разумеется, компилятор еще на стадии проверки синтаксиса не допустит, чтобы в программе были объявлены одноименные переменные в рамках одного диапазона видимости (скажем, 2 глобальных переменных X, или 2 локальных переменных X в одной и той же подпрограмме). Речь в данном случае идет о том, что произойдет, если в одной и той же программе будет 2 переменных X, одна — глобальная, а другая — локальная (в какой-либо подпрограмме). Если с основным блоком программы все ясно — в нем будет присутствовать только глобальная X, то как быть с подпрограммой? В таком случае в действие вступает правило близости, т.е. какая переменная ближе (по структуре) к данному модулю, та и есть верная. Применительно к подпрограмме ближней оказывается локальная переменная X, и именно она будет задействована внутри подпрограммы.

program Project3; var X: integer; procedure Proc1; var X: integer; begin X := 5; // Здесь значение будет присвоено локальной переменной X end; begin X := 10; // Здесь же значение будет присвоено голобальной переменной X end.

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

program Project1; procedure Proc1; procedure SubProc; begin end; begin SubProc; // Верно. Вложенная процедура здесь видна. end; begin Proc1; // Верно. Процедура Proc1 объявлена в зоне глобальной видимости SubProc; // Ошибка! Процедура SubProc недоступна за пределами Proc1. end.

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

Видимость в модулях

Все то, что мы уже рассмотрели, касалось программ, умещающихся в одном единственном файле. На практике же, особенно к тому моменту, когда мы перейдем к визуальному программированию, программы будут включать в себя множество файлов. В любом случае, программа на Object Pascal будет иметь уже изученный нами файл проекта — dpr, или основной модуль программы. Все прочие файлы будут располагаться в других файлах, или модулях (units), с типичным для Pascal расширением pas. При объединении модулей в единую программу возникает вопрос видимости переменных, а так же процедур и функций в различных модулях.

Для начала вернемся к рассмотрению структуры модуля, которая имеет ряд отличий от структуры программы. Итак, в простейшем случае, модуль состоит из названия, определяемого при помощи ключевого слова unit, и 2 секций — interface и implementation. Так вот как раз первая секция, interface, и служит для определения (декларации) типов данных, переменных, функций и процедур данного модуля, которые должны быть доступны за пределами данного модуля.

Чтобы лучше в этом разобраться, создадим программу, состоящую из 2 модулей — основного (dpr) и дополнительного (pas). Для этого сначала создайте новый проект типа Console Application, а затем добавьте к нему модуль, для чего из подменю File ‘ New выберите пункт Unit. После этого сохраните проект, щелкнув по кнопке Save All (или File ‘ Save All). Обратите внимание, что первым будет предложено сохранить не файл проекта, а как раз файл дополнительного модуля. Назовем его extunit.pas, а сам проект — miltiunits (см. Demo\Part1\Visibility). При этом вы увидите, что в части uses файла проекта произошло изменение: кроме постоянно добавляемого модуля SysUtils, появился еще один модуль — extunit, т.е. код стал таким:

uses SysUtils, extunit in ‘extunit.pas’;

Мы видим, что Delphi автоматически добавила пояснение, в каком файле находится подключаемый модуль. Это вызвано тем, что если о расположении собственных модулей Delphi все известно, то пользовательские модули могут находиться где угодно на жестком диске ПК. Но в данном случае мы сохранили и файл программы, и подключаемый модуль в одном каталоге, следовательно, их пути совпадают, и данное указание можно было бы опустить:

uses SysUtils, extunit;

Тем не менее, оставим код как есть, и приступим к разработке модуля extunit. В нем, в части implementation, напишем 2 процедуры — ExtProc1 и ExtProc2. Обе они будут делать одно и то же — выводить строку со своим названием. Например, для первой:

Теперь вернемся к главному модулю программы и попробуем обратиться к процедуре ExtProc1:

. begin ExtProc1; end.

Попытка компиляции или запуска такой программы приведет к ошибке компилятора «Undeclared identifier», что означает «неизвестный идентификатор». И действительно, одного лишь описания процедуры недостаточно, чтобы она была доступна вне своего модуля. Так что перейдем к редактированию extunit и в секции interface напишем строку:

Такая строка, помещенная в секцию interface, является объявлением процедуры ExtProc1, и делает ее видимой вне данного модуля. Отметим, что в секции interface допускается лишь объявлять процедуры, но не определять их (т.е. тело процедуры здесь будет неуместно). Еще одним полезным эффектом от объявления процедур является то, что таким образом можно обойти такое ограничение, как необходимость определения подпрограммы до ее вызова. Иначе говоря, поскольку в нашем файле уже есть 2 процедуры, ExtProc1и ExtProc2, причем они описаны именно в таком порядке — сначала ExtProc, а потом ExtProc2, то выведя объявление ExtProc2 в interface, мы сможем обращаться к ExtProc2 из ExtProc1, как это показано в листинге 6.5:

Листинг 6.5. Объявление процедур в модуле

unit extunit; interface procedure ExtProc1; procedure ExtProc2; implementation procedure ExtProc1; begin writeln(‘ExtProc1’); ExtProc2; // Если объявления не будет, то компилятор выдаст ошибку end; procedure ExtProc2; begin writeln(‘ExtProc2’); end; end.

Отметим, что теперь процедуры ExtProc2, так же, как и ExtProc1, будет видна не только по всему модулю extunit, но и во всех использующей этот модуль программе multiunits.

Разумеется, все, что было сказано о процедурах, верно и для функций. Кроме того, константы и переменные, объявленные в секции interface, так же будут видны как во всем теле модуля, так и вне него. Остается лишь рассмотреть вопрос пересечения имен, т.е. когда имя переменной (константы, процедуры, функции) в текущем модуле совпадает с таковым в подключенном модуле. В этом случае вновь вступает в силу правило «кто ближе, тот и прав», т.е. будет использоваться переменная из данного модуля. Например, если в extunit мы объявим типизированную константу Z, равную 100, а в multiunits — одноименную константу, равную 200, то обратившись к Z из модуля extunit, мы получим значение 100, а из multiunits — 200.

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

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

Некоторые стандартные функции

В Object Pascal, как уже отмечалось, имеются огромное количество стандартных процедур и функций, являющихся составной частью языка, и с некоторыми мы уже знакомы (например, приведенные в табл. 5.1 и 5.2 функции преобразования). Детальное описание всех имеющихся в Object Pascal процедур и функций можно получить в справочной системе Delphi, однако мы все-таки рассмотрим здесь некоторые из них, чтобы составить общее представление — см. таблицу 6.1.

19.11.2009, 17:50 #4
Таблица 6.1. Некоторые стандартные процедуры и функции Delphi

Синтаксис Группа Модуль Описание
function Abs(X); арифметические System Возвращает абсолютное значение числа
procedure ChDir(const S: string); управления файлами System Изменяет текущий каталог
function Concat(s1 [, s2. sn]: string): string; строковые System Объединяет 2 и более строк в 1
function Copy(S; Index, Count: Integer): string; строковые System Возвращает часть строки
function Cos(X: Extended): Extended; тригонометрические System Вычисляет косинус угла
procedure Delete(var S: string; Index, Count: Integer); строковые System Удаляет часть строки
function Eof(var F): Boolean; ввод-вывод System Проверяет, достигнут ли конец файла
procedure Halt [ ( Exitcode: Integer) ]; управления System Инициирует досрочное прекращение программы
function High(X); диапазона System Возвращает максимальное значение из диапазона
procedure Insert(Source: string; var S: string; Index: Integer); строковые System Вставляет одну строку в другую
function Length(S): Integer; строковые System Возвращает длину строки или количество элементов массива
function Ln(X: Real): Real; арифметические System Возвращает натуральный логарифм числа (Ln(e) = 1)
function Low(X); диапазона System Возвращает минимальное значение из диапазона
procedure New(var P: Pointer); размещения памяти System Создает новую динамическую переменную и назначает указатель для нее
function ParamCount: Integer; командной строки System Возвращает количество параметров командной строки
function ParamStr(Index: Integer): string; командной строки System Возвращает указанный параметр из командной строки
function Pos(Substr: string; S: string): Integer; строковые System Ищет вхождение указанной подстроки в строку и возвращает порядковый номер первого совпавшего символа
procedure RmDir(const S: string); ввод-вывод System Удаляет указанный подкаталог (должен быть пустым)
function Slice(var A: array; Count: Integer): array; разные System Возвращает часть массива
function UpCase(Ch: Char): Char; символьные System Преобразует символ в верхний регистр
function LowerCase(const S: string): string; строковые SysUtils Преобразует ASCII-строку в нижний регистр
procedure Beep; разные SysUtils Инициирует системный сигнал
function CreateDir(const Dir: string): Boolean; управления файлами SysUtils Создает новый подкаталог
function CurrentYear: Word; даты и времени SysUtils Возвращает текущий год
function DeleteFile(const FileName: string): Boolean; управления файлами SysUtils Удаляет файл с диска
function ExtractFileExt(const FileName: string): string; имен файлов SysUtils Возвращает расширение файла
function FileExists(const FileName: string): Boolean; управления файлами SysUtils Проверяет файл на наличие
function IntToHex(Value: Integer; Digits: Integer): string; форматирования чисел SysUtils Возвращает целое в шестнадцатеричном представлении
function StrPCopy(Dest: PChar; const Source: string): PChar; строковые SysUtils Копирует Pascal-строку в C-строку (PChar)
function Trim(const S: string): string; строковые SysUtils Удаляет начальные и конечные пробелы в строке
function TryStrToInt(const S: string; out Value: Integer): Boolean; преобразования типов SysUtils Преобразует строку в целое
function ArcCos(const X: Extended): Extended; тригонометрические Math Вычисляет арккосинус угла
function Log2(const X: Extended): Extended; арифметические Math Возвращает логарифм по основанию 2
function Max(A,B: Integer): Integer; арифметические Math Возвращает большее из 2 чисел
function Min(A,B: Integer): Integer; арифметические Math Возвращает меньшее из 2 чисел

Те функции, которые имеются в модуле System, являются основными функциями языка, и для их использования не требуется подключать к программе какие-либо модули. Все остальные функции и процедуры можно назвать вспомогательными, и для их использования следует подключить тот или иной модуль, указав его в uses, например, как это делает Delphi уже при создании новой программы с SysUtils:

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

Функции в действии

В целом мы уже ознакомились с несколькими десятками предопределенных процедур и функций, а так же умеем создавать собственные. Пора применить полученные знания на практике, для чего вновь вернемся к программе, рассмотренной в главе, посвященной операторам — игре «Угадай-ка». В ней, по сути, был реализован только один из рассмотренных в самом начале книги алгоритмов — угадывания числа. Что касается алгоритма управления, то на тот момент мы оставили его без внимания.

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

  1. Реализовать-таки возможность повторного прохождения игры без перезапуска программы;
  2. Добавить немного «геймплея». Иначе говоря, введем уровни сложности и подсчет очков. Новые уровни можно реализовать как повторное прохождение игры с увеличением сложности (скажем, за счет расширения диапазона загадываемых значений);
  3. В продолжение п. 2 добавить еще и таблицу рекордов, которая будет сохраняться на диске.

Поскольку часть работы уже выполнена, то для того, чтобы приступить к разработке новой версии игры (назовем ее «Угадай-ка 2.0»), мы не будем как обычно создавать новый консольный проект в Delphi, а откроем уже существующий (Ugadaika) и сохраним его под новым именем, скажем, Ugadaika2, и в новом каталоге. Таким образом, мы уже имеем часть исходного кода, отвечающую за угадывание, в частности, цикл while (см. листинг 4.5). Этот фрагмент логичнее всего выделить в отдельную процедуру, вернее даже функцию, которая будет возвращать число попыток, сделанное пользователем. Для этого создадим функцию, которая будет принимать в качестве аргумента число, которое следует угадать, а возвращаемым значением будет целое, соответствующее числу попыток. Ее объявление будет таким:

function GetAttempts(a: integer):integer;

Данная функция так же должна иметь в своем распоряжении переменную, необходимую для ввода пользователем своего варианта ответа. Еще одна переменная нужна для подсчета результата, т.е. количества попыток. В качестве первой можно было бы использовать глобальную переменную (b), однако во избежание накладок, для локального использования в функции следует использовать локальную же переменную. Что касается переменной-счетчика, то для нее как нельзя лучше подходит автоматическая переменная result. Еще одним изменением будет использование цикла repeat вместо while. Это вызвано тем, что с одной стороны, тем, что хотя бы 1 раз пользователь должен ввести число, т.е. условие можно проверять в конце цикла, а с другой мы можем избавиться от присвоения лишнего действия, а именно — присвоения заведомо ложного значения переменной b. Ну и еще одно дополнение — это второе условие выхода, а именно — ограничение на число попыток, которое мы установим при помощи константы MAXATTEMPTS:

const MAXATTEMPTS = 10;

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

Листинг 6.6. Функция GetAttempts

function GetAttempts(a: integer):integer; var b: integer; begin Result:=0; repeat inc(Result); // увеличиваем счетчик числа попыток write(#13+#10+’?:’); read(b); if (b>a) then begin write(‘Too much!’); continue; end; if (b

Теперь, когда подготовительная работа сделана, можно браться за реализацию намеченных изменений. Прежде всего, в теле программы нам потребуется цикл, который как раз и будет обеспечивать логику исполнения программы. Для него нам так же понадобятся переменные. В частности, нужны счетчик цикла, устанавливающий текущий уровень сложности, так же нужны переменные для хранения набранных очков и числа попыток, и, кроме того, не помешает заранее определить файловую переменную для таблицы рекордов и строковую — для ввода имени «рекордсмена». Итого мы получаем следующий список переменных перед основным блоком программы:

var level, score, attempt: integer; f: TextFile; s: string;

Теперь инициализируем счетчик псевдослучайных чисел (т.е. оставим randomize на месте) и инициализируем нулем значения счета и уровня:

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

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

repeat writeln(‘Level ‘+IntToStr(level)+’:’); writeln(‘From 0 to ‘+IntToStr(level*100)); attempt:=GetAttempts(random(level*100+1)); score:=score+(MAXATTEMPTS-attempt)*level; writeln(#10+’You current score is: ‘+IntToStr(score)); inc(level); until attempt>MAXATTEMPTS;

После завершения работы цикла, т.е. когда пользователь хоть раз истратит на отгадывание все 10 попыток, следует сообщить итоговый результат и сравнит его с предыдущим значением, которое следует считать из файла. Файл мы назовем records.txt, и сопоставим с переменной f:

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

if not FileExists(‘record.txt’) then begin Rewrite(f); writeln(f,’0′); // первая строка содержит число-рекорд writeln(f,’None’); // а вторая — имя последнего победителя CloseFile(f); end;

Теперь можно считать этот файл. Правда, мы упустили из виду, что нам здесь тоже нужна переменная — для считывания предыдущего рекорда. В то же время, на данный момент мы уже имеем 2 ненужных для дальнейшей работы программы переменных — attempt и level, так что вполне можно воспользоваться любой из них для этих целей. Таким образом, мы получим следующий код:

Reset(f); readln(f, attempt); readln(f,s); writeln(#10+’BEST SCORE: ‘+IntToStr(attempt)+’ by ‘+s); CloseFile(f);

Ну и последнее, чего нам остается — это проверить, является ли новое значение выше рекорда, и если да — то записать новый рекорд в файл, не забыв спросить имя игрока:

Вот, собственно, и все. Полный код получившейся программы можно увидеть на листинге 6.7, или же в файле проекта в каталоге Demo\Part1\Ugadaika2.

Листинг 6.7. Программа угадай-ка, окончательный вариант

В завершение отметим, что эта программа использует использование не только функций, но и констант, глобальных и локальных переменных, а так же циклов и операций файлового ввода-вывода. Таким образом, на текущий момент мы познакомились со всеми основами обычного, процедурного программирования. Пора двигаться дальше — к объектно-ориентированному программированию в Object Pascal!

Функции Delphi

Стандартные функции Delphi:

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

Между значением и именем функции существует зависимость. Поэтому всякая функция может быть представлена как операнд некоторого выражения (к примеру, в инструкции присваивания). Для возведения числа в n-ую степень достаточно записать

откуда ln — функция, вычисляющая натуральный логарифм числа exp(x), exp — функция, вычисляющая экспоненту в степени x, x — число, n-ую степень которого надо найти, а n — степень числа x. Каждая функция обладает следующими характеристиками: тип значений, тип параметров.

Должно существовать соответствие между типом переменной (ей присваивается определенное значение функции) и типом функции. И в то же время необходимо соответствие между типом фактического параметра данной функции (фактический параметр — это параметр, который указывается при обращении к функции) и типом формального параметра. В противном случае компилятором выводится сообщение об ошибке.

Математические функции Delphi:

Библиотеки языка Delphi включаются в себя и множество математических функций:

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

где a выражает угол в градусах; 3.1415926 означает число pi. На месте константы 3.1415926 с дробной частью для достижения большей точности чаще всего пользуются стандартной именованной константой pi. Тогда выражения для угла в пересчете в радианы будет выглядеть следующим образом:

Функции преобразования Delphi:

Наиболее частое использование функций преобразования связано с инструкциями, которые обеспечивают ввод/вывод какой-либо информации. Например, для вывода значения переменной c типом real в поле вывода диалогового окна (компонент Label), нужно провести преобразование числа в строку символов, которая собственно изображает данное число. Это можно достичь, применяя функцию FloatToStr, которая заменяет значение выражения (оно указано как параметр функции) его строковым представлением.

Пример.

В приведенном примере значение переменной m будете выведено в поле Label. В таблице ниже Вам будут представлены основные функции преобразования Delphi:

Применение функций Delphi:

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

Примеры.

Структура функции Delphi

Как организована инструкция функции в языке Delphi? В любом языке программирования на первом этапе описания функции указывается ее заголовок. Далее за заголовком программист описывает раздел объявления констант const (если таковы имеются), затем занимается описанием раздела объявления типов type, далее следует раздел объявления переменных var и, наконец, раздел инструкций.

В приведенном примере в заголовке функции вначале указывается зарезервированное слово function, а следом идет имя функции. Далее в скобках программист перечисляет список параметров, и вслед за ним, используя символ «:», указывает тип значения функции. В конце каждого заголовка стоит символ «;». После заголовка следуют раздел констант, раздел типов, раздел переменных. Внутри раздела инструкций кроме констант и переменных, описанных соответственно в разделах const и var, может находится переменная result.

Когда инструкции функции завершат свое выполнение, значению переменной result присваивается значение функции. Таким образом, среди всех инструкций функций необходимое присутствие инструкции, которая бы присваивала переменной result окончательное значение функции. Обычно подобная инструкция есть последняя исполняемая инструкция функции. Представим пример функции FuntToKg, преобразующей фунты в килограммы.

Использование замыканий и функций высших порядков в Delphi

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

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

Конструирование функций

Функции высшего порядка (ФВП) – это функции, которые оперируют функциями, принимая одну или более функций и возвращая новую функцию.
Следующий пример показывает, как с помощью ФВП можно конструировать другие функции.

Функция Negate в примере выше, является ФВП, потому что она принимает функцию IsOdd в виде аргумента и возвращает новую функцию IsEven, которая передает свои аргументы Negate и возвращает логическое отрицание значения, возвращаемого функцией IsOdd.

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

Композиция функций

Ниже приводится пример еще одной, более универсальной функции, которая принимает две функции, F и G, и возвращает новую функцию, которая возвращает результат F(G()).

Здесь функция Compose вычисляет F(G(X, Y)). Возвращаемая функция передает все свои аргументы функции G, затем передает значение, полученное от G, функции F и возвращает результат вызова F.

Частичное применение

Этот термин описывает преобразование функции с несколькими аргументами в функцию, которая принимает меньшее количество аргументов, при этом значения для опущенных аргументов задаются заранее. Этот прием вполне адекватен своему названию: он «частично применяет» некоторые аргументы функции, возвращая функцию, принимающую остающиеся аргументы.
Функция BindLeft в примере ниже берет функцию Calc, принимающую n аргументов, связывает первые k из них с наперед заданными значениями и возвращает функцию Partial, которая может принять (n-k) аргументов (первые k аргументов будут уже применены к ней).

Здесь интересен момент, когда после вызова BindLeft локальная переменная StoredArgs не прекращает свое существование и используется далее, сохраняя в себе значения аргументов, которые потом используются при вызове Partial и передаются в Calc. Этот эффект называется замыканием. При этом каждый вызов BindLeft будет порождать новые «экземпляры» StoredArgs. Замыкания использовались и в предыдущих примерах, когда в них сохранялись аргументы ФВП.
Определить частичное применение справа можно следующим образом:

Карринг

В то время как частичное применение преобразует функцию с n параметрами в функцию с n-k параметрами, применяя k аргументов, карринг декомпозирует функцию на функции от одного аргумента. Мы не передаем никаких дополнительных аргументов в метод Curry, кроме преобразуемой функции:

  • Curry(F) возвращает функцию F1, такую что.
  • F1(A) возвращает функцию F2, такую что.
  • F2(B) возвращает функцию F3, такую что.
  • F3(С) вызывает F(A, B, C)
Мемоизация

Мемоизованная функция — это функция, которая сохраняет ранее вычисленные результаты. Другими словами, для функции создаётся таблица результатов, и, будучи вычисленным при определённых значениях параметров, результат заносится в эту таблицу. В дальнейшем результат берётся из данной таблицы. Эта техника позволяет за счёт использования дополнительной памяти ускорить работу программы. Разумеется, мемоизируемая функция должна работать без побочных эффектов и ей желательно иметь дискретную область определения.
В следующем примере демонстрируется функция Memoize высшего порядка, которая принимает функцию в виде аргумента и возвращает ее мемоизованную версию.

Функция Memoize создает объект TCache для использования в качестве кэша и присваивает его локальной переменной, благодаря чему он остается доступным (через замыкание) только для возвращаемой функции. Возвращаемая функция преобразует свой аргумент в ключ. Если значение присутствует в кэше, оно просто возвращается в качестве результата. В противном случае вызывается оригинальная функция, вычисляющая значение для заданного аргумента; полученное значение помещается в кэш и возвращается.

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

Генераторы

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

Польза генераторов заключается в том, что для вычисления каждого следующего элемента не требуется вычислять всю последовательность с самого начала. Генераторы позволяют работать даже с бесконечными последовательностями, но они обеспечивают только последовательный доступ к своим элементам и не позволяют обращаться к своим элементам по индексу: чтобы получить n-e значение придется выполнить n-1 итераций.

Отложенные вычисления

Генераторы бывает удобно использовать для последовательной обработки данных — элементов списка, строк текста, лексем в лексическом анализаторе и т.д. Генераторы можно объединять в цепочки, подобно конвейеру команд в Unix. Самое интересное в этом подходе заключается в том, что он следует принципу отложенных вычислений: значения «извлекаются» из генератора (или из конвейера) по мере необходимости, а не все сразу. Эту особенность демонстрирует следующий пример, в котором исходный текст фильтруется, построчно проходя через цепочку генераторов.

Илон Маск рекомендует:  Вырезанные уголки
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL