Sum — Функция Delphi


Содержание

Sum — Функция Delphi

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

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

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

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

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

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

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

procedure Имя_процедуры(параметры);
begin
Код процедуры;
end;

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

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

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

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

var
Form1: TForm1;
A, B, Summa: Integer;
procedure Sum(A, B: Integer);

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;

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.W >
а не
Form1.Button1.W >

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

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

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

Пример. Вычисление факториала

Вычисление факториала — классическая в программировании задача на использование рекурсии. Факториал числа N — результат перемножения всех чисел от 1 до N (обозначается N!):

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

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

FAQ — Сводка функций модуля Math

Сводка функций модуля Math

Здесь я привожу полный список всех функций и процедур модуля Math. При переходе от Delphi 2 к Delphi 3 модуль Math почти не изменился, фирма Borland ввела в него только три новые функции: MaxIntVal ue, MInIntValue и Sumint. Эти функции отличаются от своих прототипов (MaxValue, MI nVal ue и Sum) лишь тем, что работают исключительно с целыми числами, не принимая и не возвращая величин с плавающей точкой. Что касается остальных функций, то большинство из них вполне очевидно. Если вам покажется иначе — что ж, садитесь за исследования. И не надейтесь, что все тайны Delphi достанутся вам на блюдечке в виде help-файла!

Тригонометрические функции и процедуры

  • ArcCos — Арккосинус
  • ArcCosh — Пиперболический арккосинус
  • ArcSIn — Арксинус
  • ArcSInh — Гиперболический арксинус
  • ArcTahn — Гиперболический арктангенс
  • ArcTan2 — Арктангенс с учетом квадранта (функция ArcTan, не учитывающая квадрант, находится в модуле System)
  • Cosh — Гиперболический косинус
  • Cotan — Котангенс
  • CycleToRad — Преобразование циклов в радианы
  • DegToRad — Преобразование градусов в радианы
  • GradToRad — Преобразование градов в радианы
  • Hypot — Вычисление гипотенузы прямоугольного треугольника по длинам катетов
  • RadToCycle — Преобразование радианов в циклы
  • RadToDeg — Преобразование радианов в градусы
  • RacIToGrad — Преобразование радианов в грады
  • SinCos — Вычисление синуса и косинуса угла. Как и в случае SumAndSquares и MeanAndStdDev, одновременная генерация обеих величин происходит быстрее
  • Sinh — Гиперболический синус
  • Tan — Тангенс
  • Tanh — Гиперболический тангенс

Delphi 7, Вычисление заданной функции. (Урок 3)

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

Итак, задача: Создать программку, для вычисления функции. Используемкомпоненты Edit, Label, LabeledEdit, Button, BitBtn . Создать иконку, организовать изменение прозрачности формы (AlphaBlend). Задать изменение курсора над разными элементами формы(Cursor). Использовать подсказки (Hint). В случае ошибки вызываем соответствующее окошко (ShowMessage).

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

А вот и сама функция:

Глядя на форму, видно что использованы компоненты Edit , Label , LabeledEdit , Button , BitBtn и TrackBar.

Поле ввода Х и надпись сверху это 2 компонента Edit и Label , а Поле ввода К и надпись над ним это LabeledEdit . На мой взгляд, в подобных случаях LabeledEdit использовать разумнее.

Кнопка решить пример это компонент Button . Далее опять Edit и Label .

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

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

Элементы Edit , Label , Button вы найдете на вкладке Standard :

Компоненты LabeledEdit и BitBtn на вкладке Additional :

Компонент TrackBar на вкладке Win 32:

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

Как ставить иконку и изменять название формы мы уже рассматривали в предыдущем уроке(начало работы в Delphi 7).

Меняем цвет формы. Выбираем форму, смотрим в Инспектор объектов. Находим там Color , жмем на выпадающий список и выбираем понравившийся цвет. Вот так все просто.

Что бы написать в Edit и LabelEdit свой произвольный текст выбираем нужный компонент, опять заглядываем в Инспектор объектов. В случае если вы выбрали Edit , просто ищите Caption и пишете нужный текст. Если же выбран LabelEdit , сначала необходимо найти EditLabel , кликнуть на плюсик, и в выпавших строчках найти Caption .

В общем говоря, что бы изменить текст на каком либо визуальном компоненте изменять следует атрибут Caption . К Button и BitBtn это тоже относится.

Ранее я говорил про некоторые встроенные возможности BitBtn . Вот сейчас пришло время показать, как ими пользоваться. Выделяем поставленный нами на форму BitBtn , в Инспекторе объектов ищем атрибут Kind , и там уже выбираем желаемую кнопочку.

Компонент получил большее распространение, так как в нем можно установить свою картинку.

Сделаем изменение курсора при наведении на разные компоненты. Комментарии наверно уже будут лишними:

Там же ищем атрибут Hint , пишем соответствующую элементу подсказку. А затем ищем ShowHint и устанавливаем значение True , если это не сделать подсказка не будет появляться при наведении курсора на компонент.

С визуальными объектами мы закончили, интерфейс программы готов, осталось куда надо вставить рабочий код. Если следовать логике, то расчеты должны проводиться после нажатия на кнопку «Решить пример». Для этого кликаем по кнопке 2 раза, и лицезреем редактор программного кода.

Сразу генерируются подобные строчки :

procedure TForm1.Button1Click(Sender: TObject);

Если вы изучали Turbo Pascal , а я на это очень надеюсь, тогда вас ничего не напугает.

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

procedure TForm1.Button1Click(Sender: TObject);

if (edit1.text=») or (labelededit1.text=») then

messagedlg(‘Введены не все данные!’,mterror,[mbOK],0) ;

Delphi функции. Учимся создавать и вызывать.

Опубиковано: 24.08.2008 г., автор: , просмотров: 157005

Delphi Функции.

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

Ну для начала немного теории.
Что же такое функция и зачем она нужна?

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

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

Пример №1
function Name (X: integer; S: String): Integer;

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

Давайте сейчас отойдем от скучной и никому не нужной теории и сразу попытаемся сделать все на практике.

Итак, Открываем Delphi создаем новый проект. Нам понадобится один компонент Button, и один компонент label оба с закладки Standart кидаем их на форму. Сделали? Отлично двигаемся дальше.
Теперь переходим к самому сладенькому — создадим нашу первую delphi функцию. Называться она будет просто: Sum, иметь она будет два входных параметра типа integer, выходной параметр будет тоже integer.

Перходим в код и после ключевого слова private пишем вот такую строчку:

function Sum (a, b: integer): integer;

Нажимем комбинацию клавиш CTRL + SHIFT + C и delphi автоматически сгенерирует вам вот такую штуку:

function TForm1.sum(a, b: integer): integer;
begin

end;

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

и все !
Как вы уже наверное догадались она будет складывать два числа.

Вызов в программе.

С созданием разобрались, осталось научиться вызывать. Создаем, обработчик событий OnClick на кнопке, между begin end пишем:

Здесь мы вызываем функцию и её результат сразу выводим в label1, т.к выходные данные у неё имеют тип integer, что бы вывести их в label нам нужно перевести их в тип string за что и отвечает конструкция inttostr()

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

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

Описание функций модуля Math

Delphi , Синтаксис , Математика


Тригонометрические функции и процедуры

  • ArcCos — Арккосинус
  • ArcCosh — Пиперболический арккосинус
  • ArcSIn — Арксинус
  • ArcSInh — Гиперболический арксинус
  • ArcTahn — Гиперболический арктангенс
  • ArcTan2 — Арктангенс с учетом квадранта (функция ArcTan, не учитывающая квадрант, находится в модуле System)
  • Cosh — Гиперболический косинус
  • Cotan — Котангенс
  • CycleToRad — Преобразование циклов в радианы
  • DegToRad — Преобразование градусов в радианы
  • GradToRad — Преобразование градов в радианы
  • Hypot — Вычисление гипотенузы прямоугольного треугольника по длинам катетов
  • RadToCycle — Преобразование радианов в циклы
  • RadToDeg — Преобразование радианов в градусы
  • RacIToGrad — Преобразование радианов в грады
  • SinCos — Вычисление синуса и косинуса угла. Как и в случае SumAndSquares и MeanAndStdDev, одновременная генерация обеих величин происходит быстрее
  • Sinh — Гиперболический синус
  • Tan — Тангенс
  • Tanh — Гиперболический тангенс

Арифметические функции и процедуры

  • Cell — Округление вверх
  • Floor — Округление вниз
  • Frexp — Вычисление мантиссы и порядка заданной величины
  • IntPower — Возведение числа в целую степень. Если вы не собираетесь пользоваться экспонентами с плавающей точкой, желательно использовать эту функцию из-за ее скорости
  • Ldexp — Умножение Х на 2 в заданной степени
  • LnXPI — Вычисление натурального логарифма Х+1. Рекомендуется для X, близких к нулю
  • LogN — Вычисление логарифма Х по основанию N
  • LogIO — Вычисление десятичного логарифмах
  • Log2 — Вычисление двоичного логарифмах
  • Power — Возведение числа в степень. Работает медленнее IntPower, но для операций с плавающей точкой вполне приемлемо

Финансовые функции и процедуры

  • DoubleDecliningBalance — Вычисление амортизации методом двойного баланса

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

Период Сумма амортизационных отчислений
1 А1:= Cost*2/Life
2 А2:= (Cost-А1)*2/Life
3 А3:= (Cost-А1-А2)*2/Life
. .
n Аn:= (Cost-А1-А2 — . — Аn-1)*2/Life
где n=Period
&nbsp

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

  • FutureValue — Будущее значение вложения

Функция вычисляет значение вклада по прошествии определенного периода времени.

  • NPeriods — количество прошедших единиц периода времени. Например, если проценты начисляются раз в год, то данный параметр определяет количество лет.
  • PresentValue — первоначальная сумма вклада.
  • В параметре Payment указывается сумма, которая будет добавляться вкладчиком ко вкладу в течение каждой единицы периода времени. Если вклад осуществляется только один раз, то Payment:=0.
  • Rate — дивиденты, начисляемые за единицу периода.
  • Параметр PaymentTime определяет, как должны начисляться проценты:

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

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

Первоначальный вклад составил 100 руб. дивиденды — 10% годовых. Ежегодно вкладчик добавляет к вкладу 50 руб. Проценты начисляются в конце года только на сумму, которая находилась на счету в начале года, т.е. проценты на сумму, которая была добавлена к вкладу в течение года будут начислены только в конце следующего года. Вычисляем сумму вклада через 5 лет.

  • InterestPayment — Вычисление процентов по ссуде

Функция вычисляет процентную ставку кредита на определенном этапе в денежном исчислении.

  • PresentValue — сумма кредита.
  • Rate — фиксированная процентная ставка кредита.
  • NPeriods — число этапов, в течение которых производятся выплаты.
  • Period — номер этапа выплат, для которого производятся вычисления.
  • FutureValue — значение суммы кредита, по истечении выплат.
  • Параметр PaymentTime определяет, происходят выплаты в начале (PtStartOfPeriod) этапа или в конце (ptEndOfPeriod).

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

  • InterestRate — Норма прибыли, необходимая для получения заданной суммы

Функция вычисляет процентную ставку инвестиций, необходимую для возврата суммы инвестиций PresentValue с дивидендами.

  • PresentValue — сумма инвестиций.
  • FutureValue — полная сумма, полученная от инвестиций. Включает возврат первоначальной суммы инвестиций и дивиденды.
  • NPeriods — количество этапов выплат.
  • Payment — сумма периодических выплат.
  • Параметр PaymentTime определяет, происходят выплаты в начале (PtStartOfPeriod) этапа или в его конце (ptEndOfPeriod).

  • InternalRateOfReturn — Вычисление внутренней скорости оборота вложения для ряда последовательных выплат

Функция вычисляет внутреннюю процентную ставку дохода от инвестиций. Массив CashFlows состоит из значений инвестиций и значений получаемого дохода за определенные периоды времени. Первое значение массива должно быть отрицательным, так как оно опредеяет первоначальную сумму инвестиций. Последующие значения могут быть отрицательными (дополнительные инвестиции), положительными (получаемый доход) или равны 0.

  • NetPresentValue — Вычисление чистой текущей стоимости вложения для ряда последовательных выплат с учетом процентной ставки

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

  • Параметр Rate — определяет процентную ставку инвестиций.
  • CashFlows — массив расчетных значений текущих платежей.
  • Параметр PaymentTime указывает, происходят выплаты в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного этапа.

  • NumberOf Periods — Количество периодов, за которое вложение достигнет заданной величины

Функция определяет количество этапов, необходимых для погашения кредита до значения определенного в параметре FutureValue.

  • PresentValue — первоначальная сумма кредита.
  • Rate — процентная ставка.
  • Payment — величина регулярных выплат.
  • Параметр PaymentTime определяет, происходят выплаты в начале (PtStartOfPeriod) этапа или в его конце (ptEndOfPeriod).

Первоначальная сумма кредита составляет 364руб. Процентная ставка 20%. Регулярные ежемесячные выплаты по 100руб производятся в конце месяца. Рассчитаем сколько месяцев необходимо для полной выплаты кредита.

  • Payment — Размер периодической выплаты, необходимой для погашения ссуды, при заданном числе периодов, процентной ставке, а также текущем и будущем значениях ссуды

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

  • PresentValue — сумма заимствования.
  • NPeriods — срок выплаты кредита. Данный пармаметр указывается количество этапов выплаты (количество лет, кварталов, месяцев и т.д.).
  • FutureValue — оставшаяся сумма кредита по истечении указанного периода.
  • Rate — величина процентной ставки кредита (ежегодная, ежеквартальная, ежемесячная и т.д. в соответствии с единицей измерения периода времени).
  • Параметр PaymentTime определяет, как происходят платежи: в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного периода.

  • PeriodPayment — Платежи по процентам за заданный период

Функция вычисляет сумму выплат основной части кредита (без учета процентов) на определенном этапе.

  • PresentValue — сумма заимствований.
  • NPeriods — срок выплаты кредита (количество этапов).
  • FutureValue — сумма кредита по прошествии указанного срока.
  • Period — номер этапа, для которого производятся вычисления.
  • Rate — процентная ставка кредита.
  • Параметр PaymentTime определяет, как происходят платежи: в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного периода.

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

  • PresentValue — Текущее значение вложения

Функция определяет значение вклада в указанный период времени.

  • Payment — первоначальная сумма вклада.
  • NPeriods — срок вклада.
  • Rate — процентная ставка.
  • FutureValue — значение, которого могут достигнуть инвестиции в определенный период.
  • Параметр PaymentTime указывает, как происходят платежи: в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного периода.

  • SLNDepreclatlon — Вычисление амортизации методом постоянной нормы

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

  • Cost — первоначальную стоимость оборудования.
  • Salvage — конечная стоимость оборудования.
  • Life — срок эксплуатации.

Для вычисления амортизации по ускоренному методу используйте функцию SYDDepreciation.

  • SYDepreclatlon — Вычисление амортизации методом весовых коэффициентов

Функция вычисляет сумму амортизационных отчислений на заданном этапе по методу ускоренной амортизации.

  • Cost — первоначальная стоимость оборудования.
  • Salvage — конечная стоимость.
  • Life — срок эксплуатации.
  • Period — номер этапа, для которого определяется сумма амортизационных отчислений.

Для вычислений по методу равномерной амортизации используйте функцию SLNDepreciation.

Статистические функции и процедуры

  • MaxIntValue — Максимальное значение в наборе целых чисел. Функция появилась в Delphi 3. ее не существует в Delphi 2
  • MaxValue — Максимальное значение в наборе чисел. В Delphi 2 функция возвращает минималъное значение
  • Mean — Среднее арифметическое для набора чисел
  • MeanAndStdDev — Одновременное вычисление среднего арифметического и стандартного отклонения для набора чисел. Вычисляется быстрее, чем обе величины по отдельности
  • MinIntValLie — Минимальное значение в наборе целых чисел. Функция появилась в Delphi 3, ее не существует в Delphi 2
  • MInValue — Минимальное значение в наборе чисел. В Delphi 2 функция возвращает максимальное значение
  • MoiiientSkewKurtosIs — Статистические моменты порядков с первого по четвертый, а также асимметрия (skew) и эксцесс (kurtosis) для набора чисел
  • Norm — Норма для набора данных (квадратный корень из суммы квадратов)
  • PopnStdDev — Выборочное стандартное отклонение. Отличается от обычного стандартного отклонения тем, что при вычислениях используется выборочное значение дисперсии, PopnVarl апсе (см. ниже)
  • PopnVarlance — Выборочная дисперсия. Использует «смещенную» формулу TotalVanance/n
  • RandG — Генерация нормально распределенных случайных чисел с заданным средним значением и среднеквадратическим отклонением
  • StdDev — Среднеквадратическое отклонение для набора чисел
  • Sum — Сумма набора чисел
  • SLimsAndSquares — Одновременное вычисление суммы и суммы квадратов для набора чисел. Как и в других функциях модуля Math, обе величины вычисляются быстрее, чем по отдельности
  • Sumint — Сумма набора целых чисел. Функция появилась в Delphi 3, ее не существует в Delphi 2
  • SLimOfSquares — Сумма квадратов набора чисел
  • Total Variance — «Полная дисперсия» для набора чисел. Это сумма квадратов расстояний всех величин от их среднего арифметического
  • Variance — Выборочная дисперсия для набора чисел. Функция использует «несмещенную» формулу TotalVanапсе/(п -1)

Статья Описание функций модуля Math раздела Синтаксис Математика может быть полезна для разработчиков на Delphi и FreePascal.

Комментарии и вопросы

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

Sum — Функция Delphi

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

Форум программистов > Delphi > БД в Delphi
Проблема с функцией SUM
Регистрация
Поиск по форуму
Расширенный поиск
К странице.

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

Здравствуйте.
Помогите разобраться. Использую Delphi 7 и FireBird 2.1
В Query пишу запрос
«select nomer, sum (kol_sut), sum (summa) from razm
group by nomer;»
вывожу данные в DBGrid. В свойстве FieldName у колонок выбираю имя поля. Открываю запрос и в DBGrid во 2ой и 3ей колонке везде написано (BYTES).

Есть еще один Query запрос «select sum(summa) as sum_summa from usl_kl;»
вывожу в DBEdit — опять выводится (BYTES)
Что это значит и как исправить?
C другими агрегаnными функциями все норамльно работает, например если написать max или count.
Тип данных суммируемых полей у всех Integer, итоговая сумма не больше 1000, а выводит (BYTES).

Пробовала еще вот так
query — field — new field
name — пишу имя поля из запроса «sum_summa», потом где type он автоматически пишет bytes, я меняю на integer, открываю запрос вылазит ошибка type mismatch for field ‘sum_summa’ , epecting: integer actual: bytes


Еще пробовала написать так Edit1.text:= query1.Fields[0].AsVariant;
Тогда в зависимости от суммы вылазит какая нибудь буква или символ

Обучающий курс. 12. Функции и процедуры в Delphi. Математические вычисления. Случайные числа

Сегодня мы поговорим о процедурах и функциях в Delphi . Что такое процедура? Это маленькая программа, выполняющая операции с указанными данными. Различают собственно процедуры и функции. Их основное отличие — процедура просто совершает какие-либо операции, а функция обязательно выдаёт какой-либо результат в результате своей работы. Существует огромное количество стандартных процедур и функций. Подпрограммы (так называют процедуры и функции) можно писать и самостоятельно, но об этом речь пойдёт позже. Сейчас нам нужно научиться работать с готовыми функциями.

Общие сведения о подпрограммах

Фактически, подпрограмма — это такая же полноценная программа, просто работает она не отдельно, не сама по себе, а включена в другую программу.
У подпрограммы всегда есть имя. Имя строится по тем же правилам, что и идентифмкатор. Как правило, имена даются вполне логичные. Например, если функция находит максимальное из нескольких чисел, то её логично назвать Max .
Подпрограммы могут иметь входные параметры. Входные параметры — это данные, которые сообщаются подпрограмме до начала её работы, а в процессе выполнения эти данные могут использоваться. Тем не менее, подпрограммы могут и не иметь входных параметров. Входные параметры также называют аргументами. Например, функции, которая узнаёт текущее время, никакие дополнительные параметры не нужны, а вот если функция считает факториал, то обязательно должно быть число, для которого он считается.
Как было сказано выше, функция выдаёт какое-то значение в результате своей работы. Процедура в общем случае значения не выдаёт.

Вызов подпрограмм

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

Обратите внимание: работа с функциями происходит как с обычными переменными, просто их значения вычисляются «на лету».

Функции математических вычислений

Эти функции работают с числовыми данными. Как правило, входным параметром является какое-то число, а выходным — результат вычисления. Практически везде аргумент является либо целым числом ( Integer ), либо вещественным ( Real ). Возвращаемое значение — тоже число. Рассмотрим некоторые из этих функций:

Abs(x) — модуль (абсолютное значение) указанного числа x . Пример: Abs(-5) = 5 .

Sin(x) — синус числа x . Здесь x — угол в радианах (не в градусах!). Пример: Sin(Pi/2) = 1 .

Cos(x) — косинус числа x . Аналогично, x — радианы. Пример: Cos(Pi) = -1 .

Exp(x) — экспонента, e x ( e в степени x ).

Ln(x) — натуральный логарифм числа x . Пример: Ln(Exp(2)) = 2 .

Sqr(x) — квадрат числа x ( x 2 ). Пример: Sqr(5) = 25 .

Sqrt(x) — квадратный корень числа x . Пример: Sqrt(64) = 8 .

Int(x) — целая часть числа x . Пример: Int(1.234) = 1 .

Frac(x) — дробная часть числа x . Пример: Frac(1.234) = 0.234 .

Round(x) — округление аргумента до ближайшего целого числа. Пример: Round(1.234) = 1 .

Trunc(x) — целая часть вещественного числа x. Пример: Trunc(1.234) = 1 .

Pred(x) — предыдущее значение x (например, для x = 2 это 1 ).

Succ(x) — следующее значение x (для x = 2 это 3 ).

Odd(x) — проверка аргумента на нечётность. Функция возвращает значение True , если аргумент является нечётным числом и False — если чётным. Пример: Odd(5) = True .

Предсказываю вопрос: в чём отличие Int() от Trunc() ? А отличие в том, что Int() возвращает число вещественного типа, а Trunc() — целочисленного .

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

Процедуры работы с числами

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

Inc(x) — увеличение аргумента на единицу. Фактически, это то же самое, что x:=x+1 . Тем не менее, рекомендуется использовать именно эту функцию, так как работает она быстрее.
Примечание: под понятием «быстрее» подразумевается, конечно, быстрота «компьютерная». Компьютер выполняет миллионы операций в секунду и для человека такие вещи незаметны.

Inc(x,n) — увеличение аргумента на число n . Эквивалентно записи x:=x+n .

На самом деле, это не две разные процедуры — просто параметр n является необязательным. Да, бывают необязательные параметры, которые можно указать, а можно и не указывать. Если они отсутствуют, то просто берётся какое-то значение по умолчанию. В данном случае n по умолчанию имеет значение 1 .

Dec(x,n) — уменьшение аргумента на n единиц. Точно также, как и в Inc , параметр n является необязательным. Эквивалентно записи x:=x-n .

В документации необязательные параметры обычно заключают в квадратные скобки, т.е. обычно пишут Inc(x , [n]) . Обратите внимание: это лишь условное обозначение, которое создано с целью узнавания, что параметр необязательный. В программном коде никаких скобок нет и быть не может.

Не хватает стандартных математических функций?

Существует дополнительный модуль с именем Math , в котором содержится большое число математических функций. Например, если нужно посчитать гиперболический арксеканс числа, то мучаться и описывать способ его вычисления вручную не придётся — есть готовая функция ArcSecH() .
Чтобы подключить модуль Math , откройте исходный код модуля. Для этого, когда открыта форма, следует нажать F12 , либо выбрать пункт меню View » Toggle Form/Unit . Далее нужно переместиться в самое начала модуля в раздел uses . В этом разделе через запятую описываются имена подключённых модулей. Как можно заметить, даже при наличии пустой формы несколько модулей уже подключены. В этот список и следует добавить Math :

Всё, теперь в Вашем распоряжении большое количество математических функций.

Пример комбинирования функций

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

Нам нужно создать программу, которая бы вычисляла значение этой функции по заданным числам x и y . Рассмотрим поэтапно элементы функции:
1) Возведение числа e в степень, модуль — функции Exp() и Abs() соответственно.
2) Натуральный логарифм — функция Ln() .
3) Число e . Часто спрашивают — как получить число e ? Ведь это, по сути, такая же константа, как и число пи . Но она не объявлена. А ответ прост: e = e 1 , поэтому e — это exp(1) .
4) Тангенс — функция Tan() .
Всё необходимое у нас есть, поэтому можно приступить к записи. Главное — не забывать заключать в скобки отдельные элементы формулы, чтобы порядок действий сохранился (в нашем примере это не потребуется).

Как возвести число в степень?

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

Способ 1. X y можно преобразовать к виду e ln(x)⋅y . Тогда возведение в степень можно записать так:

Способ 2. В модуле Math есть функция для возведения в степень — Power . У функции 2 аргумента — основание и показатель степени. Запись, соответственно, следующая :=Power(x,y);

Случайные числа

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

В Pascal (и Delphi соответственно) случайные числа генерируются функцией Random . Функция принимает один параметр, да и тот необязательный. Этот параметр позволяет указать границу диапазона, из которого будет выбрано случайное число. Итак: Random([Range: Integer]) . Если Range указан, то число выбирается из диапазона 0 ( X — само случайное число, которое будет получено). Обратите внимание, что сама граница в диапазон не включается, т.е. Random(10) никогда не выдаст число 10 , хотя 0 — запросто. Если диапазон не указан, то он считается равным единице, т.е. 0 .

Пример. Создадим форму с кнопкой, но пусть кнопка каждую секунду изменяет своё положение. Воспользуемся таймером ( TTimer , вкладка System палитры компонент). Interval оставим без изменения ( 1 сек. ), а вот в обработчике запрограммируем произвольное изменение положения кнопки на форме. Разберёмся, что нам нужно:
1) Позиция кнопки на форме. Как Вы уже знаете, за положение отвечают свойства Left и Top , которые указывают положение левого верхнего угла кнопки относительно левого верхнего угла формы. Именно этим свойствам мы будем присваивать произвольные значения.
2) Каков будет диапазон для генерации случайных чисел? Очевидно, что кнопка не должна уйти за границы формы. Значит нам нужно подключить размеры самой формы, т.е. её высоту и ширину. В данном случае будем использовать не Width и Height , а ClientWidth и ClientHeight , так как в первые свойства входят заголовок и границы формы, а это лишние пиксели, за которые кнопка может вылезти. Однако и это ещё не всё — из этих размеров мы должны вычесть соответственно ширину и высоту самой кнопки, иначе она может частично скрыться за границами.
Пишем обработчик:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

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

Отчего это происходит? Дело в том, что числа, выдаваемые функцией Random() на самом деле не являются случайными — они псевдослучайны , т.е. наблюдается повторение. К счастью, решение есть — специальная процедура Randomize() инициализирует генератор случайных чисел, который выдаёт действительно случайные числа. Вызвать эту процедуру нужно всего один раз за время работы программы — обычно это делается при запуске (например, в событии OnCreate формы). Процедура не принимает никаких параметров. Вернёмся к нашему примеру:

procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize
end ;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Теперь кнопка будет прыгать совершенно по разным точкам при каждом запуске программы.

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

Дополнительные возможности редактора кода

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

Как и было отмечено выше, второй параметр является необязательным и он заключен в квадратные скобки.

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

Если функции или процедуре входные параметры не нужны, подсказка всё равно появится и сообщит об этом:

Если после набора имени и скобки подсказка не появилась, то и при компиляции программы скорее всего возникнет ошибка. Причиной, по которой компилятор не смог найти указанную функцию или процедуру, может быть ошибка при наборе имени, либо модуль, в котором описана подпрограмма, не подключен.
Ошибки при компиляции появляются внизу окна редактора кода с указанием номера строки, где обнаружена ошибка и описанием самой ошибки.
Попробуем запросить процедуру Randomiz (например, мы случайно недописали букву » e » на конце):

Строка с ошибкой выделилась, а внизу появился её номер ( 28 ) и описание — Undeclared identifier (неописанный идентификатор).

Заключение

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

Sum — Функция 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.

21.02.2010, 14:58 #1
Таблица 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

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

Для работы с этими функциями, в разделе описания uses нужно указать математическую библиотеку Math:

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

Обучающий курс. 12. Функции и процедуры в Delphi. Математические вычисления. Случайные числа

Сегодня мы поговорим о процедурах и функциях в Delphi . Что такое процедура? Это маленькая программа, выполняющая операции с указанными данными. Различают собственно процедуры и функции. Их основное отличие — процедура просто совершает какие-либо операции, а функция обязательно выдаёт какой-либо результат в результате своей работы. Существует огромное количество стандартных процедур и функций. Подпрограммы (так называют процедуры и функции) можно писать и самостоятельно, но об этом речь пойдёт позже. Сейчас нам нужно научиться работать с готовыми функциями.

Общие сведения о подпрограммах

Фактически, подпрограмма — это такая же полноценная программа, просто работает она не отдельно, не сама по себе, а включена в другую программу.
У подпрограммы всегда есть имя. Имя строится по тем же правилам, что и идентифмкатор. Как правило, имена даются вполне логичные. Например, если функция находит максимальное из нескольких чисел, то её логично назвать Max .
Подпрограммы могут иметь входные параметры. Входные параметры — это данные, которые сообщаются подпрограмме до начала её работы, а в процессе выполнения эти данные могут использоваться. Тем не менее, подпрограммы могут и не иметь входных параметров. Входные параметры также называют аргументами. Например, функции, которая узнаёт текущее время, никакие дополнительные параметры не нужны, а вот если функция считает факториал, то обязательно должно быть число, для которого он считается.
Как было сказано выше, функция выдаёт какое-то значение в результате своей работы. Процедура в общем случае значения не выдаёт.

Вызов подпрограмм

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

Обратите внимание: работа с функциями происходит как с обычными переменными, просто их значения вычисляются «на лету».

Функции математических вычислений

Эти функции работают с числовыми данными. Как правило, входным параметром является какое-то число, а выходным — результат вычисления. Практически везде аргумент является либо целым числом ( Integer ), либо вещественным ( Real ). Возвращаемое значение — тоже число. Рассмотрим некоторые из этих функций:

Abs(x) — модуль (абсолютное значение) указанного числа x . Пример: Abs(-5) = 5 .

Sin(x) — синус числа x . Здесь x — угол в радианах (не в градусах!). Пример: Sin(Pi/2) = 1 .

Cos(x) — косинус числа x . Аналогично, x — радианы. Пример: Cos(Pi) = -1 .

Exp(x) — экспонента, e x ( e в степени x ).

Ln(x) — натуральный логарифм числа x . Пример: Ln(Exp(2)) = 2 .

Sqr(x) — квадрат числа x ( x 2 ). Пример: Sqr(5) = 25 .

Sqrt(x) — квадратный корень числа x . Пример: Sqrt(64) = 8 .

Int(x) — целая часть числа x . Пример: Int(1.234) = 1 .

Frac(x) — дробная часть числа x . Пример: Frac(1.234) = 0.234 .

Round(x) — округление аргумента до ближайшего целого числа. Пример: Round(1.234) = 1 .

Trunc(x) — целая часть вещественного числа x. Пример: Trunc(1.234) = 1 .

Pred(x) — предыдущее значение x (например, для x = 2 это 1 ).

Succ(x) — следующее значение x (для x = 2 это 3 ).

Odd(x) — проверка аргумента на нечётность. Функция возвращает значение True , если аргумент является нечётным числом и False — если чётным. Пример: Odd(5) = True .

Предсказываю вопрос: в чём отличие Int() от Trunc() ? А отличие в том, что Int() возвращает число вещественного типа, а Trunc() — целочисленного .

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

Процедуры работы с числами

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

Inc(x) — увеличение аргумента на единицу. Фактически, это то же самое, что x:=x+1 . Тем не менее, рекомендуется использовать именно эту функцию, так как работает она быстрее.
Примечание: под понятием «быстрее» подразумевается, конечно, быстрота «компьютерная». Компьютер выполняет миллионы операций в секунду и для человека такие вещи незаметны.

Inc(x,n) — увеличение аргумента на число n . Эквивалентно записи x:=x+n .

На самом деле, это не две разные процедуры — просто параметр n является необязательным. Да, бывают необязательные параметры, которые можно указать, а можно и не указывать. Если они отсутствуют, то просто берётся какое-то значение по умолчанию. В данном случае n по умолчанию имеет значение 1 .

Dec(x,n) — уменьшение аргумента на n единиц. Точно также, как и в Inc , параметр n является необязательным. Эквивалентно записи x:=x-n .

В документации необязательные параметры обычно заключают в квадратные скобки, т.е. обычно пишут Inc(x , [n]) . Обратите внимание: это лишь условное обозначение, которое создано с целью узнавания, что параметр необязательный. В программном коде никаких скобок нет и быть не может.

Не хватает стандартных математических функций?

Существует дополнительный модуль с именем Math , в котором содержится большое число математических функций. Например, если нужно посчитать гиперболический арксеканс числа, то мучаться и описывать способ его вычисления вручную не придётся — есть готовая функция ArcSecH() .
Чтобы подключить модуль Math , откройте исходный код модуля. Для этого, когда открыта форма, следует нажать F12 , либо выбрать пункт меню View » Toggle Form/Unit . Далее нужно переместиться в самое начала модуля в раздел uses . В этом разделе через запятую описываются имена подключённых модулей. Как можно заметить, даже при наличии пустой формы несколько модулей уже подключены. В этот список и следует добавить Math :

Всё, теперь в Вашем распоряжении большое количество математических функций.

Пример комбинирования функций

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

Нам нужно создать программу, которая бы вычисляла значение этой функции по заданным числам x и y . Рассмотрим поэтапно элементы функции:
1) Возведение числа e в степень, модуль — функции Exp() и Abs() соответственно.
2) Натуральный логарифм — функция Ln() .
3) Число e . Часто спрашивают — как получить число e ? Ведь это, по сути, такая же константа, как и число пи . Но она не объявлена. А ответ прост: e = e 1 , поэтому e — это exp(1) .
4) Тангенс — функция Tan() .
Всё необходимое у нас есть, поэтому можно приступить к записи. Главное — не забывать заключать в скобки отдельные элементы формулы, чтобы порядок действий сохранился (в нашем примере это не потребуется).

Как возвести число в степень?

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

Способ 1. X y можно преобразовать к виду e ln(x)⋅y . Тогда возведение в степень можно записать так:

Способ 2. В модуле Math есть функция для возведения в степень — Power . У функции 2 аргумента — основание и показатель степени. Запись, соответственно, следующая :=Power(x,y);

Случайные числа

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

В Pascal (и Delphi соответственно) случайные числа генерируются функцией Random . Функция принимает один параметр, да и тот необязательный. Этот параметр позволяет указать границу диапазона, из которого будет выбрано случайное число. Итак: Random([Range: Integer]) . Если Range указан, то число выбирается из диапазона 0 ( X — само случайное число, которое будет получено). Обратите внимание, что сама граница в диапазон не включается, т.е. Random(10) никогда не выдаст число 10 , хотя 0 — запросто. Если диапазон не указан, то он считается равным единице, т.е. 0 .

Пример. Создадим форму с кнопкой, но пусть кнопка каждую секунду изменяет своё положение. Воспользуемся таймером ( TTimer , вкладка System палитры компонент). Interval оставим без изменения ( 1 сек. ), а вот в обработчике запрограммируем произвольное изменение положения кнопки на форме. Разберёмся, что нам нужно:
1) Позиция кнопки на форме. Как Вы уже знаете, за положение отвечают свойства Left и Top , которые указывают положение левого верхнего угла кнопки относительно левого верхнего угла формы. Именно этим свойствам мы будем присваивать произвольные значения.
2) Каков будет диапазон для генерации случайных чисел? Очевидно, что кнопка не должна уйти за границы формы. Значит нам нужно подключить размеры самой формы, т.е. её высоту и ширину. В данном случае будем использовать не Width и Height , а ClientWidth и ClientHeight , так как в первые свойства входят заголовок и границы формы, а это лишние пиксели, за которые кнопка может вылезти. Однако и это ещё не всё — из этих размеров мы должны вычесть соответственно ширину и высоту самой кнопки, иначе она может частично скрыться за границами.
Пишем обработчик:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

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

Отчего это происходит? Дело в том, что числа, выдаваемые функцией Random() на самом деле не являются случайными — они псевдослучайны , т.е. наблюдается повторение. К счастью, решение есть — специальная процедура Randomize() инициализирует генератор случайных чисел, который выдаёт действительно случайные числа. Вызвать эту процедуру нужно всего один раз за время работы программы — обычно это делается при запуске (например, в событии OnCreate формы). Процедура не принимает никаких параметров. Вернёмся к нашему примеру:

procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize
end ;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Теперь кнопка будет прыгать совершенно по разным точкам при каждом запуске программы.

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

Дополнительные возможности редактора кода

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

Как и было отмечено выше, второй параметр является необязательным и он заключен в квадратные скобки.

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

Если функции или процедуре входные параметры не нужны, подсказка всё равно появится и сообщит об этом:

Если после набора имени и скобки подсказка не появилась, то и при компиляции программы скорее всего возникнет ошибка. Причиной, по которой компилятор не смог найти указанную функцию или процедуру, может быть ошибка при наборе имени, либо модуль, в котором описана подпрограмма, не подключен.
Ошибки при компиляции появляются внизу окна редактора кода с указанием номера строки, где обнаружена ошибка и описанием самой ошибки.
Попробуем запросить процедуру Randomiz (например, мы случайно недописали букву » e » на конце):

Строка с ошибкой выделилась, а внизу появился её номер ( 28 ) и описание — Undeclared identifier (неописанный идентификатор).

Заключение

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

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