Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

BEGIN

s:=0; { сумму обязательно обнуляем!!! }

i:=1; { начинаем суммировать с единицы }

WHILE s<100 DO

BEGIN

s:=s+i;

i:=i+1

END;

Label1.Caption:='Сумма превысит 100 при i='+IntToStr(i)

end;

Кстати, сумма превысит 100 при i=15, так как . Обратите внимание на операторные скобки BEGIN..END. Они позволяют поместить в тело цикла более одного оператора (разумеется, если в теле цикла только один оператор, BEGIN…END не нужны).

Рис. 1. Обозначение цикла с предусловием на блок-схемах.

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

2.2. Вечный цикл

Одна из самых больших неприятностей в программировании – зацикливание. Если при каждом проходе цикла в условии ничего не меняется, то цикл никогда не прекратится и программа "зависнет". Чтобы ее прервать насильно, нужно нажать клавиши Ctrl+C или Ctrl+Break. Вот яркий пример вечного цикла:

s:=0;

WHILE s<100 DO

i:=i+1;

В теле цикла находится только один оператор i:=i+1. Его выполнение никак не влияет на значение переменной s, которое всегда будет равным нулю и, соответственно, меньшим 100. Поэтому такой цикл никогда не прекратится.

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

2.3. Цикл с постусловием

Другая разновидность цикла отличается тем, что условие проверяется не до, а после выполнения тела цикла. Поэтому цикл с постусловием обязательно выполняется как минимум один раз. Именно поэтому он не чаще всего не годится для решения вычислительных задач: возникает лишняя итерация (проход) цикла, и, складывая 1+2+3, можно получить ответ 10=1+2+3+4.

НЕ нашли? Не то? Что вы ищете?

Цикл с постусловием записывается оператором REPEAT.. UNTIL. Его общий вид таков:

REPEAT

оператор1

оператор n

UNTIL условие;

Тело цикла составляют операторы, стоящие между REPEAT и UNTIL. Дополнительные BEGIN..END здесь не нужны – синтаксис оператора и так позволяет помещать в тело цикла несколько операторов.

Важная особенность цикла REPEAT состоит в том, что его условие является условием прекращения цикла. Цикл прерывается и программа идет дальше, как только условие становится истинным. Напомним, что в цикле WHILE все наоборот - там цикл прерывается, как только условие становится ложным.

Цикл с постусловием идеально подходит для организации контроля корректности вводимых пользователем данных. Например, мы ожидаем ввода числа от 1 до 3 (скажем, при выборе, куда вводить информацию: на экран или в файл). Как обезопасить себя на тот случай, если гнусный пользователь возьмет и введет 4 или 0? Очень просто:

VAR a:BYTE;

BEGIN

REPEAT

a:=StrToInt(LabeledEidit1.Text)

UNTIL (a>=1) AND (a<=3);

После ввода числа проверяется условие его нахождения в интервале [1;3]. Если введенное число не попадает в указанный интервал, условие будет ложным и цикл повторится до тех пор, пока условие не окажется истинным.

Обозначение цикла с постусловием на блок-схемах показано на Рис. 2.

Рис. 2. Обозначение цикла с постусловием на блок-схемах.

2.4. Цикл с переменной

Весьма часто возникает необходимость просто выполнить участок программы заданное число раз. Конечно, можно организовать такой цикл при помощи оператора WHILE. Например, нам нужно перебрать все целые числа от 10 до 20 и просуммировать их. Сделать это можно следующим образом:

VAR i: BYTE; s: WORD;

BEGIN

i:=10;

s:=0;

WHILE i<=20 DO

BEGIN

s:=s+i;

i:=i+1

END;

Такая программа работоспособна, но громоздка и некрасива. Есть способ лучше – использовать цикл с переменной FOR..TO. Его общий вид таков:

FOR переменная:=начальное значение TO конечное значение DO

оператор;

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

Перепишем нашу программу, используя оператор FOR:

VAR i: BYTE; s: WORD;

BEGIN

s:=0;

FOR i:=10 TO 20 DO

s:=s+i;

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

А что делать, если нужно перебирать не целые, а дробные значения? Единственный выход – по-прежнему перебирать целые, а в теле цикла делить их на 10,100,1000… для получения дробного числа. Например, просуммируем числа от 0.1 до 1.2 с шагом 0.1:

VAR i:BYTE; s:REAL;

BEGIN

s:=0.0; { обязательно 0.0, а не 0 – это вещественное число }

FOR i:=1 TO 12 DO

s:=s+i/10;

Иногда нужно перебирать числа в обратную сторону – от большего к меньшему. Обычный цикл FOR в случае, когда начальное значение переменной цикла меньше конечного, вообще не будет выполняться ни разу. Однако в Delphi предусмотрен вариант цикла с переменной, в котором шаг равен минус единице. Все отличие состоит в том, что слово TO заменяется на DOWNTO:

FOR i:=20 DOWNTO 10 DO…

На блок-схемах цикл с переменной изображается так, как показано на Рис. 3.

Рис. 3. Обозначение цикла с переменной на блок-схемах.

2.5. Дополнительные операторы управления циклами

В теле всех трех циклов можно применять два специальных оператора: BREAK и CONTINUE. Оператор BREAK вызывает досрочный выход из цикла. Например, вернемся к задаче суммирования ряда натуральных чисел 1+2+3+… Мы хотим узнать, на каком числе этого ряда его сумма превысит 100. Программа с BREAK выглядит так:

VAR s:WORD; { сумма }

i: WORD; { текущее число ряда }

BEGIN

s:=0; { сумму обязательно обнуляем!!! }

i:=1; { начинаем суммировать с единицы }

WHILE TRUE DO { вечный цикл?! Не совсем…}

BEGIN

s:=s+i;

i:=i+1;

IF s>100 THEN

BREAK

END;

Label1.Caption:='Сумма превысит 100 при i='+IntToStr(i)

end;

Наличие оператора BREAK позволило смело совершить программистское преступление – написать вечный цикл WHILE TRUE DO. Прерывание цикла при s>100 обеспечивает оператор BREAK.

Оператор CONTINUE, наоборот, переходит в начало цикла и начинает следующую итерацию. Это позволяет пропустить часть итераций. Например, мы хотим вывести на экран все целые числа от 1 до 100, которые не делятся на 3. Делается это так:

VAR i:BYTE

BEGIN

FOR i:=1 TO 100 DO

BEGIN

IF (i MOD 3=0) THEN

CONTINUE;

Memo1.Lines. Add(IntToStr(i))

END;

Обратите внимание на способ проверки делимости. Операция MOD вычисляет остаток от деления нацело. Скажем, 10 MOD 3=1. Если одно число нацело делится на другие, то остаток будет равен нулю. В этом случае условие в операторе IF оказывается истинным и выполняется оператор CONTINUE. Он пропускает остаток тела цикла и снова возвращается к оператору FOR. Если же значение переменной i не делится на три, тело цикла выполняется дальше.

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

2.6. Вложенные циклы

Внутри одного цикла может находиться другой, внутри его – еще один и т. д. Такая "матрешка" нужна для решения многих задач. Рассмотрим пример вложенных циклов. Пусть задана прямоугольная решетка на плоскости размером 10´20. Нужно вывести на экран координаты всех узлов такой решетки. Делается это так:

VAR i, j: WORD;

BEGIN

FOR i:=1 TO 10 DO

FOR j:=1 TO 20 DO

Memo1.Lines. Add('X='+IntToStr(i)+' , Y='+IntToStr(j))

В данном случае BEGIN…END не нужны: внутри внешнего цикла находится только один оператор FOR j:=1 TO 20. Кстати, вложенными могут быть циклы всех трех типов.

На блок-схеме приведенная программа будет выглядеть следующим образом (рис. 4):

Рис. 4. Обозначение вложенного цикла на блок-схемах.

3. Задание на работу

Разработать приложение в соответствии с полученным заданием.

4. Содержание отчета

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

5. Список использованных источников

1.  Delphi. Программирование на языке высокого уровня: учебник для вузов / . – М.[и др.]: Питер, 2007. – 640 с.

2.  Delphi 7: [наиболее полное руководство] / [и др.]. –СПб.: БХВ-Петербург, 2007. – 1216 с.

ЛАБОРАТОРНАЯ РАБОТА №10

Работа с массивами

1. Цель и задачи работы

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

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

2. Общие положения

Массив – это набор переменных одинакового типа. Обращение к конкретному элементу массива происходит по его номеру. Это позволяет обрабатывать массивы в циклах.

Для создания массива сначала необходимо определить пользовательский тип данных. Если такие типы, как REAL, INTEGER, BOOLEAN есть в Delphi всегда (они называются встроенными), то встроенных статических массивов в Delphi нет – их надо создавать самому. Новые типы данных создаются оператором TYPE, который должен находиться в разделе описаний перед оператором VAR. Оператор TYPE не выделяет память под переменные, он нужен, чтобы потом в операторе VAR компьютер знал, сколько памяти выделить.

Обычные массивы в Delphi являются статическими. Это значит, что число элементов в массиве должно быть задано как константа и известно до начала работы программы. Если нужно, например, ввести число n с клавиатуры и затем создать массив из n элементов, то необходимо применять более сложную структуру – динамический массив.

Общий вид оператора TYPE при создании типа данных "массив" такой:

TYPE имя_типа = ARRAY [размерность] OF тип_данных_элементов;

Здесь имя_типа – придуманное программистом имя нового типа данных (обычно его начинают с буквы "Т", чтобы не путать с именами переменных), тип_данных_элементов – один из встроенных типов данных, к которому будут принадлежать все элементы массива.

Размерность массива указывает, сколько элементов содержится в массиве и как они будут расположены. В простейшем случае массив будет линейным (одномерным). Для линейного массива размерность указывается в виде imin..imax (обратите внимание – две точки, а не три!). Здесь imin – индекс первого элемента массива, imax – индекс последнего. Число элементов в таком массиве равно imax-imin+1.

Например, создадим массив из 10 чисел типа REAL:

TYPE TA=ARRAY [1..10] OF REAL;

А теперь выделим под него память:

VAR a:TA;

Индексы элементов массива могут быть и нулевыми, и даже отрицательными. Единственное ограничение – суммарный размер массива в памяти не должен превышать 64Кб.

При работе с массивом в программе можно обращаться к его отдельным элементам, указывая их индекс в квадратных скобках после имени массива, например: a[1], a[i], a[j+2-1]. Нельзя присвоить значение всему массиву сразу. В приведенном примере запись a:=10 является глупостью. Значения в массив надо заносить в цикле, по одному. Например, заполним массив случайными числами:

CONST Nmax=10; { число элементов в массиве }

TYPE TA=ARRAY[1..Nmax] OF REAL;

VAR a:TA; i:BYTE;

BEGIN

FOR i:=1 TO Nmax DO

a[i]:=RANDOM

. . .

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

2.1. Многомерные статические массивы

Каждый элемент массива, в свою очередь, может являться массивом. Это позволяет создавать двумерные таблицы (матрицы), трехмерные (представьте себе прямоугольный параллелепипед), а также массивы с большим числом измерений. Зачем может понадобиться четырехмерный массив, который и представить-то сложно? Вот пример: в комнате в точках с координатами x, y,z меряется температура t. Тогда данные о температуре легче всего представить в виде четырехмерного массива [x, y,z, t].

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

TYPE TA=ARRAY[1..10, 1..5] OF REAL;

В таком массиве всего 10´5=50 элементов, а для обращения к любому из них нужно указывать два индекса: a[i, j].

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

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

CONST MaxX=10, MaxY=20;

TYPE TA=ARRAY[1..MaxX, 1..MaxY] OF REAL;

VAR a:TA; i, j: BYTE; s:REAL;

BEGIN

s:=0;

FOR i:=1 TO MaxX DO

FOR j:=1 TO MaxY DO

BEGIN

a[i, j]:=random;

s:=s+a[i, j]

END;

s:=s/(MaxX*MaxY);

Label1.Caption:=

'Максимальный элемент равен '+FloatToStrF(max, ffFixed,10,4);

2.2. Ввод-вывод массивов

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

Выноска 3 (без границы): фиксированный столбецВыноска 3 (без границы): фиксированный ряд

Выноска 3 (без границы): полосы прокруткиРис. 1– Компонент TStringGrid.

Основными свойствами компонента TStringGrid являются:

ColCount – общее число столбцов, включая фиксированные;

RowCount – общее число строк, включая фиксированные;

DefaultColWidth – ширина каждого столбца в пикселах:

DefaultRowHeight – высота каждой строки в пикселах;

FixedCols, FixedRows – число фиксированных столбцов и строк соответственно. Фиксированные столбцы и строки выделены цветом (она задается в свойстве FixedColor) и остаются на месте при прокрутке содержимого таблицы, поэтому в них очень удобно задавать заголовки столбцов и строк;

ScrollBars – управляет отображением полос прокрутки. Возможные значения: ssNone (нет), ssHorizontal (только горизонтальная полоска), ssVertical(только вертикальная полоска), ssBoth (обе полоски);

GridLineWidth – задает толщину линий сетки в пикселах.

Свойство Options представляет собой множество значений, управляющих работой TStringGrid. Среди этих значений:

goFixedVertLine – отображать вертикальные линии для фиксированных ячеек таблицы;

goFixedHorzLine– отображать горизонтальные линии для фиксированных ячеек таблицы;

goVertLine - отображать вертикальные линии для обычных ячеек таблицы;

goHorzLine - отображать горизонтальные линии для обычных ячеек таблицы;

goRangeSelect – возможность выделения диапазона ячеек, если отключен ввод данных в ячейки с клавиатуры;

goDrawFocusSelected – выделение редактируемой ячейки цветом;

goRowSizing – возможность изменения высоты строк пользователем;

goColSizing – возможность изменения ширины столбцов пользователем;

goRowMoving – возможность изменения порядка следования обычных строк при перетягивании их мышкой;

goColMoving - возможность изменения порядка следования обычных столбцов при перетягивании их мышкой;

goEditing – возможность ввода значений в ячейки таблицы пользователем с клавиатуры;

goTabs – клавиша Tab обеспечивает переход от ячейки к ячейке;

goRowSelect – выделение не отдельной ячейки, а целой строки;

goAlwaysShowEditor – постоянно включен режим редактирования содержимого ячеек и отображается текстовый курсор;

goThumbTracking – содержимое таблицы перерисовывается "на ходу", в процессе перетаскивания линеек прокрутки.

Для доступа из программы к содержимому каждой ячейки применяется массив Cells[столбец, строка]. Нумерация строк и столбцов ведется с нуля! Данные в ячейках хранятся как текст (тип данных String), поэтому при работе с числами необходимо выполнять преобразование.

Организуем ввод с клавиатуры двумерного массива вещественных чисел 4´5 элементов и найдем его максимальный элемент. Положим на форму компонент TStringGrid и установим его свойства следующим образом:

ColCount=4;

RowCount=5;

DefaultColWidth=50;

DefaultRowHeight=50;

FixedCols=0;

FixedRows=0;

ScrollBars=ssNone;

Options=[goEditing, goDrawFocusSelected]

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

VAR i, j:BYTE;

max:REAL;

begin

WITH StringGrid1 DO

BEGIN

max:=StrToFloat(Cells[1,1]);

FOR i:=1 TO ColCount-1 DO

FOR j:=1 TO RowCount-1 DO

IF StrToFloat(Cells[i, j])>max THEN

max:=StrToFloat(Cells[i, j]);

Label1.Caption:='Max='+FloatToStrF(max, ffFixed,10,4)

END

end;

Как видно, нет необходимости копировать введенные числа в статический массив – можно напрямую работать с массивом Cells.

В приведенном примере для простоты отсутствует защита от ввода неверных значений. В реальной программе, разумеется, надо ставить оператор TRY…EXCEPT.

Для вывода информации в виде массива нужно просто присваивать значения элементам массива Cells. Например, так можно заполнить заголовки (ячейки фиксированной строки). Строка

StringGrid1.Cells[0,0]:='Длина, мм';

занесет значение "Длина, мм" в левую верхнюю ячейку таблицы.

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

2.0

14.5

-15.3

1

а) одномерный массив

б) двумерный массив

Рис. 2– Файлы для хранения массивов.

Считывание делается примерно следующим образом:

TYPE TA=ARRAY[1..10] OF REAL;

VAR a:TA; f:TEXTFILE; i:BYTE;

BEGIN

ASSIGNFILE(f,'1.txt');

RESET(f);

FOR i:=1 TO 10 DO

READLN(f, a[i]);

CLOSEFILE(f);

Преобразование типов (из файла считывается текстовая строка, а в массив оно заносится как вещественное число) выполняется автоматически процедурой READLN.

Если массив двумерный, то удобно в каждой строчке файла располагать несколько его элементов, разделяя их пробелами. Давайте считаем массив 3´3 элемента, показанный на Рис. 6, б:

TYPE TA=ARRAY[1..3,1..3] OF REAL;

VAR a:TA; i, j:BYTE; f:TEXTFILE;

BEGIN

ASSIGNFILE(f,'1.txt');

RESET(f);

// Цикл по строкам файла

FOR i:=1 TO 3 DO

BEGIN

// Цикл по числам внутри строки

FOR j:=1 TO 3 DO

READ(f, a[I, j]);

// Считывание символов конца строки

READLN(f)

END;

CLOSEFILE(f);

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

2.3. Поиск максимума и минимума

Очень часто встречается задача нахождения минимального или максимального элемента в массиве. Существует много неправильных способов ее решения. А вот как выглядит правильный:

CONST Nmax=10; { число элементов в массиве }

TYPE TA=ARRAY[1..Nmax] OF REAL;

VAR a:TA; i:BYTE; max: REAL;

BEGIN

// заполнение массива

FOR i:=1 TO Nmax DO

a[i]:=RANDOM;

max := a[1];

FOR i:=2 TO Nmax DO

IF a[i]>max THEN

max:=a[i];

Label1.Caption:=

'Максимальный элемент равен '+FloatToStrF(max, ffFixed,10,4);

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

3. Задание на работу

Разработать приложение в соответствии с полученным заданием.

4. Содержание отчета

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

5. Список использованных источников

1.  Delphi. Программирование на языке высокого уровня: учебник для вузов / . – М.[и др.]: Питер, 2007. – 640 с.

2.  Delphi 7: [наиболее полное руководство] / [и др.]. –СПб.: БХВ-Петербург, 2007. – 1216 с.

ЛАБОРАТОРНАЯ РАБОТА №11

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

1. Цель и задачи работы

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

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

2. Общие положения

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

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

ЕСЛИ В ТЕКСТЕ ПРОГРАММЫ ЧТО-ТО ПОВТОРЯЕТСЯ –

ВЫ ЧТО-ТО НЕПРАВИЛЬНО ДЕЛАЕТЕ!

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

2.1. Что такое функция?

Сначала рассмотрим функции, поскольку с ними вам уже приходилось сталкиваться. В Delphi предусмотрено много встроенных функций, таких, как SQR(), SQRT(), SIN(), COS() и др. Давайте создадим пользовательскую функцию, которая будет вычислять тангенс угла. Мы хотим, чтобы в любом месте программы в формуле можно было написать TAN(x), а не делить синус на косинус.

Запись TAN(x) является вызовом функции с именем TAN. Сама же функция должна быть где-то описана. По правилам Delphi все объекты в программе описываются до их первого использования. Поэтому глобальные (доступные во всей программе) процедуры и функции описываются в разделе реализации после оператора IMPLEMENTATION.

Функцию можно представить себе как "черный ящик", в который мы что-то кладем и из которого мы что-то достаем. Любая функция имеет вход и выход. У функции тангенса на входе – значение угла в радианах, а на выходе – значение тангенса этого угла. Входных параметров может быть несколько (например, функция вычисления длины гипотенузы по двум длинам катетов) или вообще не быть (функция, возвращающая текущее время). Возвращаемое значение у функции есть всегда.

Общий вид описания функции следующий:

FUNCTION имя_функции(арг1:тип; … аргn : тип): тип_возвр_знач;

раздел локальных описаний

BEGIN

Result := значение

END;

Арг1 … аргn – это переменные, в которые заносятся значения, подаваемые функции на вход. Как и для любой другой переменной, после них через двоеточие указывается тип данных. кроме того, нужно указать тип значения, возвращаемого функцией.

2.2. Функция: туда и обратно

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

a:=StrToFloat(Edit2.Text);

b := Tan(a);

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

FUNCTION Tan(x: REAL):REAL;

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

Все переменные, описанные внутри функции или процедуры, являются ЛОКАЛЬНЫМИ. Это значит, что они никак не связаны с одноименными переменными, описанными в других частях программы. Например:

IMPLEMENTATION

VAR a: REAL; { глобальная переменная а }

FUNCTION abc(a:REAL):REAL; { локальная переменная а }

BEGIN

END;

В примере в начале всей программы выделяется память под ГЛОБАЛЬНУЮ переменную с именем a. Глобальные переменные можно использовать в любом месте в тексте программы. Они постоянно занимают память. Большое количество глобальных переменных – признак низкой квалификации программиста.

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

В момент вызова функции Tan значение, записанное в скобках после ее имени, будет занесено в локальную переменную a. Например:

x := Tan(2*b+pi);

Сначала вычисляется выражение 2*b+pi, а затем начинает выполняться функция и полученное при вычислении значение заносится в ее локальную переменную а.

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

FUNCTION abc(a:REAL):REAL;

TYPE TA=ARRAY[1..10] OF REAL;

VAR x:REAL;

BEGIN

END;

В приведенном примере в переменную a заносится значение при вызове функции, а переменная x является просто ячейкой памяти. Она никак не связана с другими переменными с именем x, описанными в других местах программы. Аналогично тип данных TA в примере действует (обычно говорят "виден") только до конца текста функции, т. е. до слова END.

После того, как в функции вычислено возвращаемое значение, самое главное – не забыть на самом деле вернуть его. Для возврата значения внутри функции всегда существует переменная с именем Result, а также с именем, совпадающем с именем самой функции. Например, внутри функции Tan живет переменная с именем Tan и типом REAL и переменная Result тоже типа Real (напомним, что тип возвращаемого значения указывается после заголовка функции). Этой-то переменной и нужно присвоить результат вычислений:

FUNCTION Tan(a:REAL):REAL;

BEGIN

Result:=SIN(a)/COS(a)

END;

Очень часто встречается следующая ошибка:

FUNCTION Tan(a:REAL):REAL;

VAR t:REAL;

BEGIN

t:=SIN(a)/COS(a)

END;

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

ВНУТРИ ФУНКЦИИ ОБЯЗАТЕЛЬНО ДОЛЖНО

ПРИСВАИВАТЬСЯ ЗНАЧЕНИЕ ПЕРЕМЕННОЙ Result

2.3. Процедуры

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

PROCEDURE имя_процедуры (арг1:тип; … аргn : тип);

раздел локальных описаний

BEGIN

операторы

END;

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

PROCEDURE Stop;

BEGIN

Form1.Close

END;

Вот как выглядит ее вызов:

BEGIN

. . .

Stop;

. . .

END;

Фактически в язык Delphi мы добавили новый оператор и назвали его Stop.

2.4. Передача данных в процедуры

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

PROCEDURE ShowMax(a, b : REAL);

VAR r:REAL;

BEGIN

r:=a-b;

IF r>0 THEN

Form1.Label1.Caption:= FloatToStr(r)

END;

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

Здесь r – локальная переменная процедуры, она существует только внутри нее. При вызове такой процедуры ей на вход надо подать два числа, например:

ShowMax(10, 2*a-45);

Обратите внимание, что здесь a – совсем другая переменная, не имеющая отношения к переменной a в заголовке процедуры. Значение 10 будет занесено в локальную переменную a, а вычисленное значение 2*a-45 – в локальную переменную b.

Такой способ передачи данных называется "по значению" (by value). У него есть два недостатка. Во-первых, он позволяет передавать информацию только в одну сторону – из вызывающей программы в процедуру. Во-вторых, при передаче больших объемов данных, скажем, больших массивов, они каждый раз полностью копируются из одной области памяти в другую, на что уходит много ресурсов компьютера.

Для решения вопроса с возвратом значения из процедуры в Delphi предусмотрен второй механизм передачи данных, называемый "по ссылке" (by reference). Его смысл состоит в том, что в процедуру передается адрес переменной, которая описана в вызывающей программе. Зная адрес, процедура может напрямую занести в эту переменную требуемое значение.

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

 

PROCEDURE Hypot (a, b:REAL; VAR c:REAL);

Выноска 2 (с границей): возможность изменения 

параметра c внутри процедуры
 

VAR h:REAL;

Выноска 2 (с границей): выделение памяти под локальную переменнуюBEGIN

h:= SQR(a)+SQR(b);

c:=SQRT(h)

END;

Весь фокус заключается в слове VAR, стоящем в заголовке перед именем переменной-аргумента c. Слово VAR в заголовке процедуры не имеет никакого отношения к выделению памяти. Оно указывает на то, что указанную после него переменную можно изменять внутри процедуры и измененное значение вернется в вызывающую программу.

При обращении к процедуре Hypot третьим ее параметром обязательно должно идти имя переменной. Следующая запись правильна:

VAR h:REAL;

. . .

BEGIN

. . .

Hypot(a+b, 45, h)

Величина гипотенузы будет записана в переменную h.

А так делать нельзя:

 

Hypot(a+b, 45, h*4)

Выражение h*4 не является именем переменной и в него нельзя записать значение.

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

CONST Nmax=20;

TYPE TA=ARRAY[1..Nmax] OF REAL;

VAR a:TA; i:BYTE; min:REAL;

PROCEDURE FindMin(ar:TA; n:BYTE; VAR min:REAL);

VAR i:BYTE;

BEGIN

min:=ar[1];

FOR i:=2 TO n DO

IF ar[i]<min THEN

min := ar[i]

END;

BEGIN

FOR i:=1 TO Nmax DO

a[i]:=RANDOM;

FindMin(a, Nmax, min);

Label1.Caption:=FloatToStrF(min, ffFixed,10,4)

END.

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

FUNCTION FindMax(a:ARRAY OF REAL):REAL;

VAR i:WORD;

BEGIN

Result:=a[Low(a)];

FOR i:=Low(a)+1 TO High(a) DO

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10