Основы программирования. Теория

Язык: Delphi Pascal.

Синтаксис и семантика языков программирования. Алфавит языка Delphi Pascal. Cинтаксические диаграммы

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

Семантика – правила, определяющие смысл синтаксически корректных предложений.

Ясная или «интуитивно-понятная» семантика – семантика, позволяющая без большого труда определять смысл программы или «читать» ее.

Алфавит языка программирования Delphi Pascal включает:

  1. латинские буквы без различия строчных и прописных + символ _ (считается как буква);
  2. арабские цифры: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9;
  3. шестнадцатеричные цифры: 0..9, а..f или A..F;
  4. специальные символы: + - * / = := ; и т. д.;
  5. служебные слова: do, while, begin, end и т. д.

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

Syntax diagram example

Представление данных в Delphi Pascal: константы и переменные

В языке Delphi Pascal все данные подразделяются на константы и переменные:

Константы – данные, не изменяемые в процессе выполнения программы.

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

Литералы – константы, указанные непосредственно в тексте программы.

Примеры литералов:

  1. -25, 2.5, 0.1e6 – числовые литералы;
  2. $2a – шестнадцатеричное число;
  3. true, false – логические константы;
  4. 'd', #65 = 'A' – символьные константы;
  5. 'abcd' – строковая константа;
  6. nil – адресная константа.

Поименованные константы – константы, обращение к которым выполняется по имени. Объявляются в разделе описаний: const <имя>[: <тип>] = <значение>;.

const
    min: byte = 0;
    max = 100;
    center = (max - min) div 2;

Переменные – поименованные данные, которые могут изменяться в процессе выполнения программы. Объявляются также в разделе описаний: var <имя>[, <имя>]: <тип>;

var
    a, b: Integer;
    c: Real;

Для наложения или же создания абсолютной переменной (которая находится по заданному или абсолютному адресу в памяти) используется служебное слово absolute:

var <имя>: <тип> absoulute <адрес>:<смещение>; или

var <имя>: <тип> absoulute <переменная>;.

//Наложение по абсолютному адресу:
var
    a: Word absolute $0000:$00FF;  // адрес_сегмента:смещение
    c: Byte;
    b: Real absolute c;  // наложение на ранее определенную переменную;
       // обычно используется, когда тип переменной, которая тут `c`, может быть разным. 
       // (см. нетипизированные параметры)

Тип – описатель данных, который определяет:

  1. диапазон изменения значения переменной, задавая размер ее внутреннего представления;
  2. множество операций, которые могут выполняться над этой переменной.

Для объявления новых типов данных используется конструкция: type <имя> = <тип>;:

type
    Int = LongInt;
    Date = 1..31;  // объявление нового типа данных

var
    d1: Date;  // объявление переменной этого типа

Классификация скалярных типов данных, операции над ними

Классификация типов данных языка

  1. Тип
    1. Простой
      1. Порядковые
        1. Целые;
        2. Логические;
        3. Символ;
        4. Перечисление;
        5. Отрезок (Интервал);
      2. Вещественный
        1. Вещественные;
        2. Большое целое;
    2. Структурный
      1. Массив;
      2. Строка;
      3. Запись;
      4. Множество;
      5. Файл;
      6. Указатель;

* Зависит от платформы. В Delphi Pascal:

type
    Integer = LongInt;  // FreePscal: SmallInt
    Real = Double;      // FreePascal: 6-byte
    // Extended (10 bytes)
    Char = WideChar;    // since Delphi 2009, FreePascal: ANSIChar

** Идентификаторы отсутствуют в Delphi Pascal.

Источники: wiki.freepascal.org 1, wiki.freepascal.org 2, delphibasics.ru.

Процедуры и функции порядковых типов данных

  1. Функция ord(x: ordinal): int – возвращает номер значения по порядку (не применима к 64-битным аргументам)
    ord('A')  // 65
    
  2. Функция pred(x: ordinal): int – возвращает предыдущее значение.
    Процедура dec(var x: int) – уменьшает значение переменной на 1.
    x := 5;
    pred(x); // 4
    dec(x);  // 4
    
  3. Функция succ(x: ordinal): int – возвращает следующее значение.
    Процедура inc(var x: int) – увеличивает значение переменной на 1.
    x := 5;
    succ(x); // 6
    inc(x);  // 6
    
  4. Функция high(x) – возвращает самое большое значение типа переменной x или максимальный размер типа строки или массива переменной x.
    Функция low(x) – возвращает самое малое значение типа переменной x или минимальный размер типа строки или массива переменной x.
    var
        x: ShortInt;
        s: String[42];
        m: array [5..35] of ShortInt;
    {...}
    writeLn(low(x), ', ', high(x));  // -128, 127
    writeLn(low(s), ', ', high(s));  // 0, 42
    writeLn(low(m), ', ', high(m));  // 5, 35
    

Операции над скалярными типами данных.

  1. Арифметические операции – применяют к вещественным и целым константам и переменным:
    + - *
    /  {вещественное деление}
    div, mod  {целочисленное деление и остаток от целочисленного}
    
  2. Операции отношения – применяют к числам, символам, строкам для сравнения. Результат – логическое значение:
    не равно равно
    <> =
    нестрогие строгие
    < <=
    > >=
  3. Логические операции – применяют к логическим значениям. Результат логическое значение:
    var
        a, b: Boolean;
    {...}
    a := true; 
    b := false;
    a and b {false};  not a {false};  a xor b {true}
    a or b {true};    not b {true};   a xor a {false}
    
  4. Побитовые (поразрядные) операции – выполняются поразрядно, применяют к целым. Результат целое число.
    not, and, or, xor, shr (сдвиг вправо), shl (сдвиг влево).
    var 
        a: Byte;
    {...}
    a := 5;
    not a   {побитовая инверсия (НЕ)   : 00000101 -> 11111010};
    a shl 2 {побитовый сдвиг влево на 2: 00000101 -> 00010100};
    

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

(int – абстрактный целочисленный тип, float – абстрактный вещественный тип)

Имя функции Тип принимаемого значения Тип возвращаемого значения Описание
abs(x) int или float так же, как аргумент Модуль числа (абсолютное значение числа)
sqr(x) int или float так же, как аргумент Квадрат числа (x2x^2)
sqrt(x) int или float float Квадратный корень числа (x\sqrt{x})
exp(x) int или float float Экспонента числа (exe^x)
ln(x) int или float float Натуральный логарифм числа
sin(x) int или float float Синус числа
cos(x) int или float float Косинус числа
arctan(x) int или float float Арктангенс числа

Работа с вещественными числами и генератором псевдослучайных чисел

Функция Описание
int(x: float): int Целая часть числа x
frac(x: float): int Дробная часть числа x
randomize Инициализация генератора псевдослучайных чисел
random: float Случайное число r в диапазоне: 0≤r<10 \le r \lt 1
random(x: int): int Случайное целое число r в диапазоне: 0≤r<x0 \le r \lt x

Совместимость типов данных и операции преобразования типов

Правила вычисления выражений. Приоритеты

  1. Порядок выполнения операций определяется приоритетами и скобками:

    Операции Приоритет (порядок выполнения)
    Унарные +, -, not, @, ^ 1
    *, /, div, mod, and, shr, shl 2
    +, -, or, xor 3
    Логические =, <>, <, >, <=, >= 4

    Примеры:

    1. x(x+2)y(y−1)⇒x∗(x+2)/y/(y−1)=x∗(x+2)/(y∗(y−1))\dfrac{x(x+2)}{y(y-1)} \Rightarrow x*(x+2) / y\textcolor{red}{/} (y-1) = x*(x+2) / \textcolor{red}{(}y*(y - 1)\textcolor{red}{)}
    2. (a<b) and (b>=1)\textcolor{red}{(}a < b\textcolor{red}{)} \space and \space \textcolor{red}{(}b >= 1\textcolor{red}{)}
  2. При выполнении арифметических операций над числами различных типов автоматически осуществляется неявное преобразование:

    • целого и вещественного типов – к вещественному,
    • с разными интервалами представлений – к типу с большим интервалом.
    var
        a: Single;
        k: Integer;
    {...}
    a / k;  // число k преобразуется к типу Single
    
  3. При сравнении вещественных чисел из-за их неточного представления проверку равенства и неравенства следует осуществлять с явным указанием допуска.

    var
        x, y: Single;
    {...}
    x <> y  ==>  abs(x - y) > 1e-10  
    x = y   ==>  abs(x - y) < 1e-10  
    

Оператор присваивания :=

Используется для изменения значений переменных.
Формат: <идентификатор_переменной> := <выражение>;

var
    v: Integer;
    a, b: Single;
{...}
    a := v * b / 2.0;

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

По правилам совместимы:

  1. все целые типы между собой;
  2. все вещественные типы между собой;
  3. отрезок базового типа и базовый тип;
  4. два отрезка одного и того же базового типа;
  5. символ и строка.

Неявное преобразование типов

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

var
    l: LongInt; 
    e, x: Extended; 
    i: Integer; 
    r: Single;

begin
{...}
r := i * e / (x + l);  // тип результата выражения `i * e / (x + l)` - Extended,
// но так как тип переменной `r` - Single, то результат автоматически преобразуется в Single.

Если результат не умещается в разрядную сетку переменной, то автоматически генерируется ошибка «Переполнение разрядной сетки».

Исключение! Для получения ошибки переполнения при работе с целыми числами необходимо установить опции компилятора Overflow checking {$Q+} и Range checking {$R+}.

При работе с целочисленными типами:

Явное преобразование типов

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

Условный оператор – оператор условной передачи управления – if ... else

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

Формат: if <логическое_выражение> then <оператор> [else <оператор>];

Оператором может быть как простой оператор, так и составной оператор (блок операторов в операторных скобках begin...end)

Оператор выбора – case

Оператор позволяет программировать несколько вариантов решения.

Формат:

case <выражение> of
    <константа/диапазон>: <оператор>;
    [<константа/диапазон>: <оператор>; ...]
    [else <оператор>;]
end;

Пример:

case 1 + 2 * j of
    3:         z:=sin(x);
    -1..1, 10: z:=cos(x);
    else       z:=0;
end;

Операторы циклов Delphi Pascal

Операторы организации циклов

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

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

Поисковый цикл имеет два выхода: нашли и перебрали все и не нашли.

Счётный цикл for-loop

Блок-схема: for-loop-diagramm

Выполняется конкретное количество раз.

Формат:
for <переменная-счётчик> := <значение_от> to <значение_по> do <оператор>; (если от < по)
for <переменная-счётчик> := <значение_от> downto <значение_по> do <оператор>; (если от > по)

Пример:

var i: Integer;
{...}
for i := 1 to 10 do
    begin
        x := x + 1;
        e := e / 10;
    end;

Цикл-пока while-do

Блок-схема:
while-do-diagramm

Выполняется, пока условие истинно.

Формат: while <логическое_выражение> do <оператор>;

Пример:

while abs(e) >= 1e-5 do
    begin
        x := x + 1;
        e := e / 10;
    end;

Цикл-до repeat-until

Блок-схема:
repeat-until-diagramm

Выполняется, до тех пор, пока условие не станет истинно (то есть пока условие ложно).

Формат:

repeat 
    <оператор>;
    [<оператор>; ...]
until <логическое выражение>;

Пример:

repeat
    x := x + 1;
    e := e / 10;
until abs(e) < 1e-5;

Поисковый цикл. Неструктурная и структурная реализации поискового цикла.

В лекциях конкретного мало было, поэтому попробую расписать самостоятельно …

Поисковый цикл – особый вид цикла, который имеет два случая выхода:

  1. нашли,
  2. перебрали все элементы, но не нашли.

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

Неструктурная реализация поискового цикла

Примерный общий вид неструктурной реализации поискового цикла:

var
    found: boolean;

{...}
found := false;
while true do
    begin
        {операторы, выполняемые до проверки}
        
        if {проверка на то, нашли ли необходимое} then begin
                found := true;  // ставим флаг, что нашли
                break;          // прерываем цикл
            end;
        if {проверка на то, не перебрали ли всё} then
            break;
        
        {операторы, выполняемые после проверки}
    end;

if found then
    {нашли}
else
    {не нашли}

Структурная реализация поискового цикла №1 (через while-do)

Примерный общий вид структурной реализации поискового цикла:

var
    found: boolean;

{...}
{операторы, выполняемые до проверки}
found := {нашли ли необходимое};
while not (found) and not ({не перебрали ли всё}) do
    begin
        {операторы, выполняемые после проверки}
        {операторы, выполняемые до проверки}
        if {нашли ли необходимое} then
                found := true;  // ставим флаг, что нашли
    end;

if found then
    {нашли}
else
    {не нашли}

Структурная реализация поискового цикла №2 (через repeat-until)

Примерный общий вид структурной реализации поискового цикла:

var
    found: boolean;
{...}

found := false;
repeat
    {операторы, выполняемые после проверки}
    {операторы, выполняемые до проверки} // !!! 
                                         // тут отличается от первой структурной реализации:
                  // выполнится в первый раз в любой случае, в отличие от первой реализации!
    if {нашли ли необходимое} then
        found := true;                   // ставим флаг, что нашли
until (found) or ({всё перебрали});

if found then
    {нашли}
else
    {не нашли}

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


Массивы Delphi Pascal

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

Формат: array [<типы индекса>] of <тип элемента> (типы индекса перечисляются через запятую, если их больше одного).

Количество типов индексов задает размерность массива.
Тип индекса – порядковый – определяет доступ к элементу.
Тип элемента – любой кроме файла, в том числе массивы, строки и т.п.
Массив в памяти не может занимать более 2 Гб (из-за технических ограничений).

Кажется, потому, что это связано с технической проблемой выделить для 32-битной системы один кусок памяти больше, чем для 32-битного указателя, то есть 32 бита⇒макс.размер 232=429496729632\spaceбита \Rightarrow макс. размер\space 2^{32} = 4294967296 для одного сегмента, но половина предназначена для системы. Поэтому в языке Pascal установлено жёсткое ограничение на 2 ГБ памяти.

Примеры объявления массива:

type 
    mas = array[1..10] of Integer;

var 
    a: array[1..5] of Integer;
    с: array[’A’..’C’, -5..-3] of Byte;
    b: array[Byte] of Char;

Инициализация массива при объявлении:

var
    a: array[1..5] of Real = (0, -3.6, 7.8, 3.789, 5.0);
    b: array[Boolean, 1..5] of Real = ((0, -3.6, 7.8, 3.789, 5.0),
                                       (6.1, 0, -4.56, 8.9, 3.0));

Операции над массивами

  1. Операция присваивания (только для массивов одного типа):
    var
        a, b: array[Boolean] of Real;
        c: array[Boolean] of Real;
    {...}
    b := a;
    c := a; // ОШИБКА! Их типы считаются разными, так как объявлены раздельно, 
            //         несмотря на то, что по факту они идентичны!
    
  2. Доступ к элементу массива (прямой и косвенный доступ):
    var
        a: array[Char, Boolean] of Real;
    {...}
    a[’A’, true] := 5.1; {прямой доступ}
    {...}
    ch := ’B’; 
    b := false;
    a[ch, b] := 3; {косвенный доступ: значения индексов находятся в переменных}
    
  3. Ввод/вывод массивов осуществляется поэлементно:
    var
        a: array[1..5] of Real;
    {...}
    for i := 1 to 5 do
        read(a[i]);
    readLn; {обрабатывает последний Enter}
    
    Вывод матрицы:
    var
        a: array[1..5, 1..7] of Real;
    {...}
    for i := 1 to 5 do
        begin
            for j := 1 to 7 do
                write(a[i][j]);   // также можно a[i, j]
                     {a[i][1], a[i][2], a[i][3], a[i][4], a[i][5], a[i][6], a[i][7]}
            writeLn;  {переход на следующую строку}
        end;
    

Строки Delphi Pascal

Строка – последовательность символов (фактически, массив символов).

Формат (обычно): string или string[<размер>], где размер – максимальная длина строки.

Внутренний формат:
Internal string format


Здесь нужно сделать небольшое отступление насчёт длинных строк

С более новых версий языка Pascal существует несколько видов строк, поэтому тип string зависит от платформы.

В Delphi Pascal (с версии Delphi 2009):

Тип Максимальная длина Размер Размер символа Описание
ShortString 255 256 байт 1 байт Является псевдонимом для string[255] (с фиксированной длиной)
AnsiString ≈231\approx2^{31}* 4 Б – 2 ГБ* 1 байт Длинные, динамические строки, но с однобайтовыми символами
UnicodeString ≈230\approx2^{30}* 4 Б – 2 ГБ* 2 байта Длинные, динамические строки с двухбайтовыми символами. В среде разработки RAD Studio string (без размера!) является псевдонимом для UnicodeString
WideString ≈230\approx2^{30}* 4 Б – 2 ГБ* 2 байта Кажется, какой-то старый костыль, который был до UnicodeString. Использовать не рекомендуется

При использовании string[<размер>] используется тип ShortString, но с размером <размер>, то есть строка фиксированной длины, меньшей 256, и символом, размером в 1 байт.
При использовании string (без размера) используется по умолчанию UnicodeString.

* Ограничение в 2 ГБ по тем же причинам, что и с массивами.

Также включить или отключить (кажется, только для старых версий) переопределение string на длинные строки можно с помощью опций компилятора:

Включить Выключить
{$H+} {$H-}
{$LONGSTRINGS ON} {$LONGSTRINGS OFF}
Источник: docwiki.embarcadero.com

Примеры описания строк:

var 
    s1, s2: string[40]; 
    s3: string;
type
    S40 = string[40];
    ST = string;

var
    s1, s2: S40;
    s3: ST;
var
    s: string[40] = 'String-constant';
    s1: string = '';

Операции над строками

  1. Присваивание строк:

    s1 := 'ABCD';
    s1 := s2;
    s1 := 'A';
    s1 := ''; {пустая строка}
    
  2. Обращение к элементу:

    s1[5]  {прямое}
    s1[i]  {косвенное}
    
  3. Конкатенация (сцепление) строк:

    str := str + 'A';
    str := 'A' + 'BC';
    
  4. Операции отношения – выполняется попарным сравнением кодов символов, результат определяется по отношению кодов первых различных символов:

    s1 := 'ABCD'; 
    s2 := 'ABCA';
    b := s1 > s2;  // true, так как 4: 'D' > 'A'
    'T' < 'Ta';    // true, так как размер первой строки меньше
    
  5. Ввод-вывод строк:

    readLn(s1);  // Строка вводится до Enter (переноса строки, пропуская сам перенос) 
                 // или максимальной длины
    writeLn(s1);
    

Стандартные процедуры и функции для работы со строками

  1. Функция length(str: string): word – возвращает длину строки str:

    n := length(s);
    
  2. Процедура delete(var str: string; i, k: int) – удаляет k символов строки str, начиная с i-го символа:

    s := 'aabbbbccccc';
    delete(s, 3, 4);  // s = 'aaccccc''
    
  3. Процедура insert(sub: string; var str: string; i: int) – вставляет подстроку sub в строку str, начиная с символа с номером i:

    my_string := 'aaacc';
    sub_str := 'bbbb';
    insert(sub_str, my_string, 4);  // 'aaabbbbcc'
    insert('Pas', my_string, 6);    // 'aaabbPasbbcc'
    
  4. Процедура str(x[:w[:d]], var str) – преобразует результат выражения x, в строку str, содержащую запись этого числа в виде последовательности символов (как при выводе):

    x := -5.67;
    str(x:7:3, s);  // s = ' -5.670'
    
  5. Процедура val(str, var x, var status_code) – преобразует строку str с записью числа в виде последовательности символов во внутреннее представление целого или вещественного числа и помещает его в переменную x. В целочисленной переменной status_code процедура возвращает код ошибки:

     var
        s: string;
        a: real;
        code: integer;
    
    {...}
    repeat
       write('Input a: ');
       readLn(s);
       val(s, a, code);
       if code <> 0 then
           writeLn('Wrong input!');
    until code = 0;
    
  6. Функция copy(str: string; i, k: int): string – возвращает фрагмент строки str, длиной k символов, начиная с i-го символа:

    str := 'qqqEEEEEEuuuuu';
    s := copy(str, 4, 6);    // 'EEEEEE'
    
  7. Функция pos(sub, str: string): int – возвращает номер позиции первого вхождения подстроки sub в строку str. Если вхождение не найдено, то функция возвращает 0:

    str := 'qqqEEррEEuuuuu';
    i := pos('EE', str);      // i = 4
    j := pos('X', str);      // j = 4
    
  8. Функция upCase(c: char): char – возвращает символ, соответствующий символу верхнего регистра для c, если таковой имеется, либо сам символ c, если для него не определен символ верхнего регистра.

Кажется, противоположной функции downCase или lowerCase нет.

Множества Delphi Pascal

Множество – неупорядоченная совокупность неповторяющихся элементов.

Формат: set of <базовый_тип>.
Тип элементов – порядковый, но количество элементов не должно превышать 256. (То есть нельзя использовать Word, Integer, UInt64 и т. д., так как они явно больше 256)

type
    Digits = set of 1..100;
    SetChar = set of Char;
    Letter = set of 'a'..'z';

var
    mychar: SetChar;
    mydig: Digits;
    simst: Letter;
    // or
    number: set of 1..31;
    cif: set of 0..9;
    codes: set of #0..#255; 

Конструкторы и инициализация множеств

Конструкторы множеств – константы множественного типа:

[] -- пустое множество;
[2, 3, 5, 7, 11] -- множество чисел;
[’a’, ’d’, ’f’, ’h’] -- множество символов;
[1, k] -- множество чисел, переменная k должна содержать число;
[2..100] -- множество содержит целые числа из указанного интервала;
[k..2 * k] -- интервал можно задать выражениями;
[red, yellow, green]- множество перечисляемого типа.

Инициализация множеств при объявлении:

type
    SetNum = set of Byte;
var 
    s: SetNum = [1..10];

Операции над множествами

  1. Присваивание:

    A := B;
    A := [];
    
  2. Объединение, пересечение и дополнение:

    • A + B (A∪BA \cup B) – объединение множеств А и B – множество, состоящее из элементов, принадлежащих множествам А и B.
    • A * B (A∩BA \cap B) – пересечение множеств А и B – множество, состоящее из элементов, принадлежащих одновременно и множеству А и множеству B.
    • A - B (A∖BA \setminus B) – дополнение множества А до B – множество, состоящее из тех элементов множества А, которые не принадлежат множеству B.
    [1, 2] + [3, 4] = [1, 2, 3, 4];
    [1..10] * [3, 8, 9 ,15, 23, 45] = [3, 8, 9];
    [1..15] - [3, 8, 9, 15, 23, 45] = [1, 2, 4..7, 10..14]
    [red, blue, green, black] * [blue, magneta, yellow] = [blue]
    
  3. Операции отношения:

    • A = B (A=BA = B) – проверка совпадения множеств А и B (если совпадают – true).
    • A <> B (A≠BA \not= B) – проверка не совпадения множеств А и B (не совпадают – true).
    • A <= B (A⊆BA \subseteq B) – проверка нестрогого вхождения A в B (если входит – true).
    • A > B (A⊃BA \supset B) – проверка строгого вхождения B в A (если входит – true).
  1. Проверка вхождения элемента во множество:
    x in A (x∈Ax \in A) – входит ли элемент x в множество A (если входит – true).
    [1, 2] + [3, 4] = [1, 2, 3, 4];
    [1..10] * [3, 8, 9 ,15, 23, 45] = [3, 8, 9];
    [1..15] - [3, 8, 9, 15, 23, 45] = [1, 2, 4..7, 10..14]
    [red, blue, green, black] * [blue, magneta, yellow] = [blue]
    

Ввод-вывод элементов множеств

Значения множественного типа нельзя вводить и выводить!

Записи Delphi Pascal

Запись – это структура данных, образованная фиксированным числом разнотипных компонентов, называемых полями записи

Формат:

type
    <имя> = record
        <переменные>: <тип>;               // фиксированная часть
        {--//--}
        [case [тэг:] [набор_типов] of      // вариантная часть
            <тип>: (
                <переменные>: <тип>;
                {--//--}
            );
            {--//--}
        ]
    end;

Примеры:

var
    rec1: record
        day: 1..31;
        month: 1..12;
        year: Word;
    end;
type
    Data = record
        day: 1..31;
        month: 1..12;
        year: Word;
    end;

var 
    rec1: Data;
    birthDay: Data = (day: 30; month: 6; year: 1973);
type
    EntryType = (Book, Magazine);
    Entry = record
        autor, title: String;
        year : 1..2000;
        case tag: EntryType of
            Book: (publisher, city: String);
            Magazine: (
                magName: String; 
                volume, issue: Integer
            );            // case не имеет своего end'а и пишется последним.
    end;

var
    e: Entry;
{...}
e.tag := Book;               // вариант записи: Book
e.autor := 'Thomas Hobbes';  
e.title := 'Leviathan';  
e.year := 1651;  
// Как бы предназначены для варианта Book записи Entry:
e.publisher := 'Andrew Crooke';  
e.city := 'London';
Подробнее про вариативную часть: informatics.mccme.ru

Операции над записями

  1. Присваивание записей одного типа:
    type
        Date = record
            day: 1..31;
            month: 1.12;
            year: Word;
        end;
    
    var
        a, b: Date;
        c: Date;
        d, e: record day: 1..31; month: 1.12; year: Word; end;
    
    {...}
    a := b;
    b := c;
    a := c;
    d := e;
    
    a := d; // ОШИБКА! Считаются разными типами!
    e := c; // ОШИБКА! Считаются разными типами!
    
  2. Доступ к полям записи
    a.day := 21;      {точечная нотация}
    
    with a do         {оператор доступа}
        day := 27;  
    with b do begin   {оператор доступа c блочным оператором}
        day := 22;  
        month := 6;
        year := 1983;
    end;
    
  3. Ввод и вывод записей осуществляется по полям.

Процедуры и функции

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

Общий вид программного блока: Заголовок блока -> Раздел описаний -> Раздел операторов

Различия между процедурой и функцией

Процедура

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

Общий вид:

procedure <идентификатор> [(<паратметры>)];
[var
   <локальные переменные>;]
begin
    <операторы>;
end;

Пример заголовка

procedure alpha(x: Integer);

Функция

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

Общий вид:

function <идентификатор> [(<паратметры>)]: <возвращаемый тип>;
[var
   <локальные переменные>;]
begin
    <операторы>;
end;

Пример заголовка:

function func(x: Integer): Double;

Чтобы вернуть какое-то значение из функции, в процессе её выполнения необходимо присвоить системной переменной <идентификатор_функции> (или result, при включенном расширенном синтаксисе {$X+}) это необходимое значение.

Пример возврата значения:

{$X+}
function hypot(a, b: Double): Double;
var
    c_sqr, c: Double;

begin
    c_sqr := sqr(a) + sqr(b);
    c := sqrt(c_sqr);
    result := c;  // или `hypot := sqrt(c_sqr);`
    writeLn('Hypot is ', c);
end;

Локальные и глобальные переменные, законы «видимости» идентификаторов

Классы переменных Время жизни Доступность
Глобальные – объявленные в основной программе От запуска до завершения программы Из любого места программы, включая подпрограммы*
Локальные – объявленные в подпрограмме От вызова подпрограммы до возврата управления Из подпрограммы и подпрограмм, вызываемых из нее*

* – при отсутствии перекрытия имен

Способы передачи данных в подпрограмму на Delphi Pascal

Подпрограмма может получать данные из основной программы:

  1. неявно – с использованием свойства доступности глобальных переменных;
  2. явно – через параметры.

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

Формальные и фактические параметры подпрограмм Delphi Pascal

Параметры, описанные в заголовке – формальные.

При вызове подпрограммы необходимо определить фактические значения этих параметров – аргументы (константы и переменные).

Вот только кто вообще их так называет?
Обычно их называют параметрами и аргументами соответственно.

Формальные и фактические параметры должны соответствовать по количеству, типу и порядку:

function proc(a: Integer; b: Single): Byte;
{...}

n := proc(5, 2.1);  // a = 5, b = 2.1

Типы параметров, const/var-переменные

Структурные типы параметров: параметры-строки, параметры-массивы.

Структурные типы параметров должны быть предварительно объявлены.

type
    IntArr = array[1..100] of Integer;
    MyStr = string[25];

function sum(arr: IntArr; n: Integer): integer;
{...}

procedure test_string(str: MyStr);
{...}

var
    my_arr: IntArr;
    my_str: MyStr;
    {...}

begin
    {...}
    writeLn('Sum(arr) = ', sum(my_arr, n));
    test_string(my_str);
end.

Принципы разработки универсальных подпрограмм

Открытые массивы

Открытый массив – конструкция описания типа массива без указания типа индексов.
Используется только при объявлении формальных параметров.

array of Single;
array of Integer;

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

Индексы открытого массива всегда начинаются с 0-го.

Размер можно:

Открытые строки

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

Чтобы избежать его необходимо использовать «открытые» строки (openstring).

Или можно использовать параметр {$P+}, который сам будет делать так, что параметрами будут открытые строки

Пример. Программа, формирующая строку из букв латинского алфавита.

unit Stroka;

interface
    procedure add(var s: Openstring);

implementation
    procedure add;
    var 
        ch: Char;
    
    begin
        ch := s[length(s)];
        s := s + chr(succ(Ord(Ch)));
    end;
end.
program Ex4_6;

{$APPTYPE CONSOLE}

uses
    SysUtils, Stroka in 'Stroka.pas';

var
    s: String[26];
    i: Integer;

begin
    s := 'A';
    for i := 2 to 26 do
        add(s);
    writeLn(s);
    readLn;
end.

Протестировав такое, у меня без этих “открытых” строк проблем не возникло. Скорее всего, из-за того, что сейчас, в новых версиях, обычные строки всегда считаются “длинными” (UnicodeString), поэтому и проблем сейчас с передачей строк нет:
Note: The $P directive is obsolete. Now the default string is long (UnicodeString). You should not have to use {$OPENSTRINGS} in any new applications you write.
Источник: docwiki.embarcadero.com

Нетипизированные параметры.

Нетипизированные параметры – параметры-переменные, тип которых при объявлении не указан.

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

  1. автоопределенное преобразование типов:

    procedure proc(var a);
    {...}
    b := Integer(a) + 10;
    
  2. наложенное описание переменной определенного типа:

    type
        TType = (TReal, TInteger);
    
    procedure proc(var a; t: TType);
    var
        r: Real absolute a;
        i: Integer absolute a;
    
    {...}
        if t = TReal then
            {используем переменную `r`}
        else
            {используем переменную `i`}
    

    По факту, тут a является ссылкой на какой-то адрес в памяти, но нужный размер в памяти нам неизвестен. С помощью наложения мы делаем несколько видов переменных, накладывая на адрес переменной a разные типы.

Параметры процедурного типа

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

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

type
    Proc = procedure (a, b, c: Real; var d: Real);
    UnaryFunc = function(x: Double): Double;

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

{...}

function fun1(x: Double): Double;
{...}

procedure test_function(f: UnaryFunc);
begin
    if f(0) > 0 then  // какая-то проверка функции
        {...}
end;

var
    f: UnaryFunc;

{...}
f := fun1;
test_function(f);  // или test_function(fun1);

Модули Delphi Pascal

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

Ресурсы – переменные, константы, описания типов и подпрограммы.

Все ресурсы, определенные в модуле делят на:

  1. внешние – предназначенные для использования другими программами и модулями.
  2. внутренние – предназначенные для использования внутри модуля.

Структура модуля:

unit <имя_модуля>;  // Имя модуля должно совпадать с 
                    // именем файла, в котором он описан.

interface
    <интерфейсная_секция>

implementation
    <секция_реализации>

[initialization
    <секция_инициализации>

[finalization
    <секция_завершения>]]
end.

Подключение модуля к программе

Подключение модуля к программе осуществляется по имени:

uses <имя_модуля1>, <имя_модуля2>,  {...};

Объявление модулей в файле проекта

Только для проектов (Delphi, Lazarus). Для обычных программ формата .pas не работает.

Если:

то необходимо указать местонахождение модуля:

uses  Strings in 'C:\Classes\Strings.pas'; {абсолютный путь}
// или
uses  Strings in '..\Strings.pas';         {относительный путь}

Законы видимости идентификаторов. Доступ к «перекрытым» идентификаторам

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

Для доступа к перекрытым ресурсам модуля используют точечную нотацию: <имя_модуля>.<имя_ресурса>

Пример:
identificators_intersection

unit A;
    interface
        var x: Real;  // !!!
        {...}
end.
program ex;
uses A;
var x: Integer;       // !!!
begin
    x := 10;
    A.x := 0.45;
    {...}

Рекурсия

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

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

Косвенная рекурсия требует предопределения forward:

procedure B(j: Byte); forward;

procedure A(j: Byte);
begin
    {...} B(i); {...}
end;

procedure B;
begin
    {...} A(i); {...}
end;

Структура рекурсивной подпрограммы

enter image description here

«Операторы после вызова», выполняются после возврата управления из рекурсивно вызванной подпрограммы.

Фрейм активации

Каждое обращение к рекурсивной подпрограмме вызывает независимую активацию этой подпрограммы.

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

Фрейм активации включает

Пример.
Задача: переворот заданной строки.

  1. ///               pointer: 4 bytes;  string[255]: 256 bytes
    function reverse1(const st: string): string;
    begin
        if length(st) = 0 then 
            result := ‘‘
        else
            result := reverse1(copy(st, 2, length(st) - 1)) + st[1];
    end;
    

    Фрейм активации: V=4+256 +<служебная область> ≈272 байтV = 4 + 256\space+ <служебная\spaceобласть> \space\approx 272\spaceбайт

  2. ///              pointer: 4 bytes; longint: 4 bytes
    procedure reverse2(var ss: string; n: integer);
    var
        temp: char;
    
    begin
        if n <= length(ss) div 2 then 
            begin
                temp := ss[n];
                ss[n] := ss[length(ss) - n + 1];
                ss[length(ss) - n + 1] := temp;
                reverse2(ss, n + 1);
            end;
    end;
    

    Фрейм активации: V=4+4 +<служебная область> ≈21 байтV = 4 + 4\space+ <служебная\spaceобласть> \space\approx 21\spaceбайт

Древовидная рекурсия. Перестановки

A
B
C
AB
AC
BA
BC
CA
CB
ABC
ACB
BAC
BCA
CAB
CBA

enter image description here

Динамическая память

Минимальная адресуемая единица памяти – байт.

Физический адрес АфА_ф – номер байта оперативной памяти.
Адресация по схеме «база+смещение»: Аф=Аб+АсмА_ф = А_б + А_{см},
где АбА_б – адрес базы – адрес, относительно которого считают остальные адреса;
АсмА_{см} – смещение – расстояние от базового адреса до физического.

Указатель – тип данных, используемый для хранения смещений.
В памяти занимает 4 байта, адресует сегмент размером V=232=4V = 2^{32} = 4 ГБ.
Базовый адрес = адрес сегмента.

Типизированные и нетипизированные указатели

Различают указатели:

Объявление типизированного указателя: ^<идентификатор_базового_типа>

Объявление нетипизированного указателя: Pointer

type
    TPI = ^Integer;
 
 var
     pi: TPI;
     pr: ^Real;
     p: Pointer
type
    PP = ^Person;    // Можно объявлять указатель типа, который объявлен следом
    Preson = record
        name: String;
        next: PP;
    end;
var
    r: ^Integer = nil;

Операции с указателями

  1. Присваивание. Допускается присваивать указателю только значение того же или неопределенного типа.

    var
        p1, p2: ^Integer;
        p3: ^Real;
        p: Pointer;
    {...}
    p1 := p2;
    p := p3;
    p1 := p;
    p1 := nil;
    p := nil;
    {нельзя:
    p3 := p2;
    p1 := p3;}
    
  2. Получение адреса: оператор @. Результат операции – указатель типа pointer – может присвоен любому указателю.

    var
        i: Integer;
        pi: ^Integer;
    
    {...}
    pi := @i;
    
  3. Доступ к данным по указателю (операция разыменования). Полученное значение имеет тип, совпадающий с базовым типом данных указателя. Нетипизированные указатели разыменовывать нельзя.

    j := pi^;
    pi^ := pi^ + 2;
    
  4. Операции отношения: проверка равенства = и неравенства <>.

    sign := p1 = p2;
    if p1 <> nil then {...}
    

Функции для работы с указателями

  1. Функция addr(x): Pointer – возвращает адрес объекта x, в качестве которого может быть указано имя переменной, функции, процедуры. Фактически, addr(x) ≡\equiv @x.

    ptr := addr(obj); // <-> ptr := @obj;
    
  2. Функция assigned(const p): Boolean – проверяет, присвоено ли значение указателю. Фактически, assigned(p) ≡\equiv p <> nil.

    ptr := addr(obj); // <-> ptr := @obj;
    
  3. Функция ptr(address: Integer): Pointer – преобразует число в указатель.

    var
        a: Integer;
        p: Pointer;
    
    {...}
    p := ptr(a);
    

Динамическое распределение памяти

Управление выделением и освобождением памяти осуществляется посредством специальных процедур и функций:

  1. Процедура new(var p: ^<тип>) – выделяет память для размещения переменной, размер определяется типом указателя.

  2. Процедура dispose(var p: ^<тип>) – освобождает выделенную память.

    var
        pi: ^Integer;
    
    {...}
    writeLn(assigned(pi)); // false
    new(pi);
    pi^ := 5;
    dispose(pi);
    writeLn(assigned(pi)); // true (так как pi <> nil)
    
  3. Процедура getMem(var p: Pointer; size: Integer) – выделяет указанное количество памяти и помещает ее адрес в указатель.

  4. Процедура freeMem(var p: Pointer[; size: Integer]) – освобождает выделенную память.

  5. Процедура sizeOf(x): Integer – возвращает размер объекта в байтах.

    type
        ByteArr = array[1..100] of Byte
    var
        buffer: ^ByteArr;
    
    {...}
    getMem(buffer, sizeOf(ByteArr));
    buffer^[1] := 125;
    {...}
    freeMem(buffer, sizeOf(Byte) * 100); 
    

Динамические структуры данных

Структуры данных
Последовательные
Древовидные
Сетевые

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

Динамические линейные структуры

  1. Очередь – структура данных, реализующая: добавление – в конец, а удаление – из начала.
  2. Стек – структура данных, реализующая: добавление и удаление с одной стороны.
  3. Дек – структура данных, реализующая: добавление и удаление с двух сторон.

Списки

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

Элемент списка состоит из двух частей: информационной и адресной.

Информационная часть содержит поля данных.

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

Списки
Линейные
Кольцевые
Древовидные
N-связные

Виды списков

Виды списков

Односвязные списки

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

но

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

Бинарные сортированные деревья

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

binary_tree

Вершины, из которых не выходит ни одной ветви, называют листьями.

Сортированные бинарные деревья, строятся по правилу:
ключевое поле левого поддерева должно содержать значение меньше, чем в корне,
а ключевое поле правого поддерева – значение больше или равное значению в корне.

Пример:
binary_tree_example

Работа с файлами

Файловая система

Файл – поименованная последовательность элементов данных (компонентов файла), хранящихся, как правило, во внешней памяти.

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

Полное имя файла включает:
<Имя диска>:<Список имен каталогов>\<Имя файла>.<Расширение>

Имя файла в Windows составляют из строчных и прописных букв латинского и русского алфавитов, арабских цифр и некоторых специальных символов, например, символов подчеркивания _ или доллара $

Файлы Delphi Pascal

Файл языка Pascal – последовательность однотипных компонентов: файл записей, файл целых чисел, файл строк.

В зависимости от типа компонентов различают три типа файлов: типизированные, текстовые и нетипизированные.

Количество компонентов файла при объявлении файловой переменной не указывается.

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

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

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

Указатель файла

Доступ к компонентам файла осуществляется через указатель файла.

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

После вывода последнего компонента файла система пишет специальную запись – маркер «Конец файла» (байт #26).

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

Описание файловых переменных

  1. Типизированные файлы: file of <тип>
  2. Текстовые файлы: text
  3. Нетипизированные файлы: file
type
    IntFile = file of Integer;

var
    fi: IntFile;
    ft: text;
    fb: file;

{...}

Использование файлов в качестве параметров подпрограмм

Файлы можно передавать в подпрограмму только через параметры-переменные.

type
    FF = file of Integer;

procedure print(var f1: FF);

Работа с файлами

Работа с файлами включает:

Инициализация файловой переменной

Процедура assign или assignFile (var f; path: String) (предпочтительнее) – связывает файловую переменную f с файлом, имя которого указано в строке path.

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

type
    FI1 = file of Integer;

var
    f1, f2: FI1;

{...}
assign(f1, ’F1.dat’);            {файл в текущем каталоге, относительный путь}
assignFile(f2, ’d:\iva\a.dat’);  {файл в другом каталоге, абсолютный путь}

Открытие файла

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

  1. Процедура reSet(var f) – открывает файл для чтения данных.
    Устанавливает указатель файла на первый компонент. Если файл не существует, выдается сообщение об ошибке.

  2. Процедура reWrite(var f) – открывает файл для записи.
    Если указанный файл существовал, то он уничтожается без выдачи предупреждения пользователю, иначе он создается и указатель устанавливается на начало.

  3. Процедура append(var f: Text) – открывает текстовый файл для добавления данных.
    Указатель файла устанавливается на конец файла.

Контроль операций ввода-вывода

Функция IOResult(): Word – возвращает код завершения операции ввода-вывода: 0 – если операция прошла нормально; код ошибки, если нет. Функция применяется при отключенном контроле операций ввода-вывода {$I-}.

Пример. Проверка существования файла.

var
    f: file of char;

begin
    assignFile(f,’a.dat’); {инициализация ф. п.}
    {$I-}  {отключение контроля ошибок ввода-вывода}
    reSet(f); {открытие файла для чтения}
    {$I+}  {включение контроля ошибок ввода-вывода}
    if IOResult <> 0 then
        writeLn('File was not found')
    else
        writeLn('File was found');
    {...}

Обработка компонентов файла

Основные операции над компонентами – операции записи и чтения. На базе этих операций выполняют более сложные операции:

Операции записи и чтения для каждого типа файла осуществляется по-своему.

Закрытие файла

Процедура close или closeFile(var f) - выполняет закрытие файла. При этом вновь созданный файл регистрируется в каталоге.

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

Связь файловой переменной с файлом при закрытии сохраняется, поэтому при продолжении обработки повторно процедуру assignFile() можно не выполнять.

Стандартные процедуры и функции обслуживания файлов (библ. System)

  1. Процедура reName(var f; name: string) – выполняет переименование файла f, где name – новое имя файла.
    При совпадении нового имени файла с ранее существовавшим выдается сообщение об ошибке.

  2. Процедура erase(var f) – выполняет удаление файла. Перед уничтожением файл должен быть закрыт.

  3. Функция EOF(var f): Boolean – проверяет конец файла, возвращает TRUE, если указатель стоит после последней записи и FALSE, если нет.

Другие функции обслуживания файлов см. Help.


Текстовые файлы

Текстовый файл – файл, компонентами которого являются символьные строки переменной длины, заканчивающиеся специальным маркером – маркером «Конец строки».

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

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

Стандартные текстовые файлы

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

INPUT – файловая переменная для обозначения файла данных, вводимых с клавиатуры;
OUTPUT – файловая переменная для обозначения файла данных, выводимых на экран.

Процедуры и функции обработки текстовых файлов

  1. Функция EOLn([var f]): Boolean – возвращает TRUE, если во входном текстовом файле достигнут маркер конца строки; при отсутствии файловой переменной проверяется файл INPUT, связанный с клавиатурой.

    • При работе с клавиатурой функция EOLn возвращает TRUE, если последним считанным был символ #13.
    • При работе с диском функция EOLn возвращает TRUE, если следующим считанным будет символ #13.
  2. Процедура read([var f: text;] v1, v2, {...} vn) – обеспечивает ввод символов, строк и чисел. При вводе чисел пробелы и символы табуляции игнорируются. Если файловая переменная не указана, то ввод осуществляется из файла INPUT.

  3. Процедура readLn([var f: text;] v1, v2, {...} vn) – осуществляет ввод символов, строк и чисел. После чтения последней переменной оставшаяся часть строки до маркера конца строки пропускается так, что следующее обращение к readLn или read начинается с первого символа новой строки.

  4. Процедура write([var f: text;] v1, v2, {...} vn) – осуществляет вывод одного или более выражений типа CHAR, STRING, BOOLEAN, а также целого или вещественного типов. При выводе числовых значений последние преобразуются в символьное представление. Если файловая переменная не указана, то вывод осуществляется в файл OUTPUT.
    Любой параметр из списка вывода может иметь формат:
    <Параметр> [: <Целое1> [: <Целое2> ]].

  5. Процедура writeLn([var f: text;] v1, v2, {...} vn) – осуществляет вывод в текстовый файл. Если файловая переменная не указана, то вывод осуществляется в файл OUTPUT.
    Выводимая строка символов завершается маркером конца строки. Если список вывода не указан, то в файл передается только маркер конца строки.

  6. Процедура seekEOLn([var f]): Boolean – пропускает пробелы и знаки табуляции до маркера конца строки или до первого значащего символа и возвращает TRUE, при обнаружении маркера. Если файловая переменная не указана, то функция проверяет файл INPUT.

  7. Процедура SeekEOF([var f]): Boolean – пропускает все пробелы, знаки табуляции и маркеры конца строки до маркера конца файла или до первого значащего символа и возвращает TRUE при обнаружении маркера. Если файловая переменная отсутствует, то функция проверяет файл INPUT.


Типизированные файлы

Типизированный файл – файл, все компоненты которого одного типа, заданного при объявлении файловой переменной.

Компоненты хранятся на диске во внутреннем (двоичном) формате.

Типизированный файл можно открыть для записи и чтения. Файл, открытый для записи, может использоваться для чтения. В файл, открытый для чтения, можно писать.

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

Процедуры и функции обработки типизированных файлов

  1. Процедура read(var f; c1, c2, {...} cn) – осуществляет чтение компонентов типизированного файла. Список ввода содержит одну или несколько переменных того же типа, что и компоненты файла. Если файл исчерпан, обращение к процедуре вызывает ошибку ввода-вывода.

  2. Процедура write(var f; c1, c2, {...} cn) – осуществляет запись компонентов в типизированный файл. Список вывода содержит одно или более выражений того же типа, что и компоненты файла.

  3. Процедура seek(var f; numcomp: LongInt) – осуществляет установку указателя файла на компонент с номером numcomp.

  4. Функция fileSize(var f): LongInt – возвращает количество компонентов файла. Может использоваться для установки на конец файла совместно с seek():

    seek(f, fileSize(f));
    
  5. Функция filePos(var f): LongInt – возвращает порядковый номер компонента, который будет обрабатываться следующим

  6. Процедура truncate(var f) – выполняет «усечение» файла до компонента, на который указывает указатель.


Нетипизированные файлы

Нетипизированными называют файлы, объявленные без указания типа компонентов.

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

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

Нетипизированный файл можно открыть для записи и для чтения:

reSet(var f[; recsize: Word]);
reWrite(var f[; recsize: Word]);

где recsize – размер записи файла в байтах. Длину записи задают кратной 512 байт, например: 1024, 2048. Если длина записи не указана, она принимается равной 128.

Процедуры и функции обработки нетипизированных файлов

  1. Процедура blockRead(var f: file; var buf; count: Word[; var res: Word]) – осуществляет чтение блока записей из файла в буфер buf.
    Параметр res будет содержать количество фактически обработанных записей. Если последняя запись – неполная, то значение параметра res ее не учтет.

  2. Процедура blockWrite(var f: file; var buf; count: Word[; var res: Word]) – осуществляет запись блока из буфера buf в файл.


Дополнительные процедуры и функции для работы с файлами

  1. function changeFileExt(const fileName, extension: String): String – изменяет существующее расширение файла на указанное.

  2. procedure chDir(const s: String); overload;
    procedure chDir(p: PChar); overload; – изменяет текущий каталог (каталог по умолчанию).

  3. function createDir(const dir: String): Boolean – создает новый каталог.

  4. function deleteFile(const fileName: String): Boolean – удаляет указанный файл.

  5. function directoryExists(const directory: String): Boolean – проверяет существование каталога по указанному адресу.

  6. function diskFree(drive: Byte): Int64 – возвращает объем в байтах свободного пространства на указанном диске:
    0 – устройства по умолчанию; 1 – диск А; 2 – диск В и т.д. Функция возвращает -1, если указанный диск не существует.

  7. function diskSize(drive: Byte): Int64 – возвращает объем памяти указанного диска.

  8. function fileExists(const fileName: string): Boolean – проверяет существование файла по указанному адресу.

  9. function fileSearch(const name, dirList: String): String – ищет файл в указанных через точку с запятой каталогах, если не находит, то возвращает пустую строку.

  10. function findFirst(const path: string; attr: Integer; var f: TSearchRec): Integer – ищет в каталоге первый файл с указанной маской и атрибутами;

  11. function findNext(var F: TSearchRec): Integer – ищет следующие файлы.

  12. function getCurrentDir: String – возвращает имя текущего каталога.

  13. function forceDirectories(dir: String): Boolean – создает каталоги и подкаталоги.

  14. function removeDir(const dir: String): Boolean – удаляет указанный пустой каталог.

  15. function setCurrentDir(const dir: String): Boolean – устанавливает текущий каталог.

Объектно-ориентированное программирование

История

«Стихийное» программирование

«Стихийное» программирование – до середины 60-х годов ХХ века – технология отсутствует – программирование – искусство создания программ – в конце периода появляется возможность создания подпрограмм – используется процедурная декомпозиция.

Структурный подход

Структурный подход к программированию - 60-70-е годы ХХ века – технология, представляющая собой набор рекомендаций и методов, базирующихся на большом опыте работы:

Модульный подход

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

Объектный подход к программированию

C середины 80-х до наших дней.

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

Компонентный подход

Компонентный подход – с конца 90-х годов ХХ века (COM-технология, Corba, SOAP) – подключение объектов через универсальные интерфейсы – развитие сетевого программирования – появление CASE-технологий.

Диаграмма состояний интерфейса пользователя

Структурная декомпозиция

Процедурная декомпозиция – процесс разбиения программы на подпрограммы.

Структурной называют декомпозицию, если:

Объектная декомпозиция

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

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

Объект предметной области

Объект предметной области характеризуется:

Состояние – совокупность значений характеристик объекта, существенных с т. з. решаемой задачи.

Поведение – совокупность реакций на сообщения.

Реализация объектов предметной области

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

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

Объект-переменная – переменная типа «класс».

Методы построения классов

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

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

    Если при наследовании какие-либо методы переопределяются, то такое наследование называется полиморфным.

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

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

Средства объектно-ориентированного программирования

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

type 
    TRoom = object
        length, width: Single;
        function square: Single;  {прототип функции}
    end;

function TRoom.square;
begin
    result := length * width;
End;

var a: TRoom = (length: 3.5; width: 5.1);

Любой метод неявно получает параметр self – ссылку (адрес) на поля объекта, и обращение к полям происходит через это имя.

function TRoom.square;
begin
    result := self.length * self.width;
End;

При необходимости эту ссылку можно указывать явно:

@self – адрес области полей данных объекта.

Объявление объектов класса

var 
    a: TRoom; {объект А класса TRoom}
    b: array[1..5] of TRoom;  {массив объектов типа TRoom}

type pTRoom=^TRoom;  {тип указателя на объекты класса TRoom}

var  pC: pTRoom;  {указатель на объекта класса TRoom}

Для динамического объекта необходимо выделить память:

new(pC);

, а после его использования – освободить память:

dispose(pC);

Обращение к полям и методам аналогично доступу к полям записей:
Примеры:

v := a.length;
s := a.square;
s := s + b[i].square;
pC^.length := 3;

Над объектами одного класса определена операция присваивания. Физически при этом происходит копирование полей одного объекта в другой методом «поле за полем»:

Инкапсуляция – ограничение доступа к полям и методам

Модификатор доступа Доступность
public поля и методы, доступны из других модулей
private поля и методы, доступны только в пределах модуля
protected поля и методы, доступны только в классах потомках
published поля и методы, видимы в инспекторе объектов

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

unit Room;

interface
    type TRoom = object
        private                     // !!!
            length, width: Single;
        public 
            function square: Single;
            procedure init(l, w: Single);
    end;
    procedure test(room: TRoom);

implementation
    function TRoom.square;
    begin
        result := length * width; 
    end;
    
    procedure TRoom.init;
    begin 
        length := l;
        width := w;
    end;

    procedure test(room: TRoom);
    begin
        writeLn(room.length, ' ', room.width);   // !!!
    end;
    
end.
program  Ex_7_02;

{$APPTYPE CONSOLE}

uses  SysUtils, Room in 'Room.pas';

var a: TRoom;

begin
    a.init(3.5, 5.1);
    // writeLn('Room: length = ', a.length:6:2,   // ERROR!
    //        '; width = ', a.width:6:2);         // ERROR!
    test(a);
    writeLn('Square = ', a.square:8:2);

    readLn;
end.

Наследование

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

program Ex_07_03;

{$APPTYPE CONSOLE}

uses  SysUtils, Room  in 'Ex_08_02\Room.pas';

type  
    TVRoom = object(TRoom)
        height: Single;
        function V: single;
        procedure newInit(l, w, h: single);
    end;

procedure TVRoom.newInit;
begin
    init(l, w);
    height := h;
end;

function TVRoom.V;
begin
    result := square * height;
end;

var
    a: TVRoom;
begin
    a.newInit(3.4, 5.1, 2.8);
    writeLn('Square = ', a.square:6:2);
    writeLn('V = ', a.V:6:2);
    readLn;
end.

Присваивание объектов иерархии

Допустимо присваивать переменной типа базового класса значение переменной типа объекта производного класса.

var
    a: TRoom;
    b: TVRoom;

{...}
a := b; {допустимо}
b := a; {не допустимо!}

Присваивание указателей в иерархии

Допустимо указателю на объект базового класса присваивать адреса объекта производного класса.
Однако при этом возникает проблема «невидимых» полей.

Проблема «невидимых» полей

var
    pC: ^TRoom;
    e: TVRoom;

{...}
pC := @e;
pC^.length := 3.1;
pC^.height := 2.7;  {ошибка!}
type
    pTVRoom = ^TVRoom;

var
    pC: ^TRoom;
    e: TVRoom;

{...}
pC := @e;
pTVRoom(pC)^.height := 2.7;

Композиция

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

program Ex_7_04;

{$APPTYPE CONSOLE}
uses
    SysUtils,
    Room in 'Ex_08_02\Room.pas';

type
    TFlat = object
        n: byte;
        rooms: array[1..15] of TRoom;
        function flatSquare(): Single;
        procedure init(an: Byte; const ar: array of TRoom);
    end;

procedure TFlat.init;
var
    i: Byte;
begin
    n := an;
    for i := 1 to n do
        rooms[i].init(ar[i - 1].length, ar[i - 1].width);
end;

function TFlat.flatSquare;
var
    s: Single;
    i: Integer;
begin
    s := 0;
    for i := 1 to n do
        s := s + rooms[i].square();
    result := s;
end;

var
    mas: array[1..3] of TRoom = ((length: 2.5; width: 3.75), 
                                 (length: 2.85; width: 4.1),
                                 (length: 2.3; width: 2.8));
    f: TFlat;

begin
    f.init(3, mas);
    writeLn('S flast = ', f.flatSquare());
    readLn;
end.

Наполнение (агрегация)

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

agregation

program Ex_7_05;

{$APPTYPE CONSOLE}
uses
    SysUtils,
    Room in 'Ex_08_02\Room.pas';

type
    TBRoom = object(TRoom)
        pB: ^TRoom;
        function BSquare(): Single;
        procedure initAll(l, w: single; lb: single = 0; wb: single = 0);
    end;

procedure TBRoom.initAll;
begin
    init(l, w);
    if (lb = 0) or (wb = 0) then
        pB := nil
    else
        begin
            new(pB);
            pB^.init(lb, wb);
        end;
end;

function TBRoom.BSquare;
begin
    if pB = nil then
        result := square()
    else
        result := square() + pB^.square;
end;

var
    b: TBRoom;
begin
    b.initAll(3.4, 5.1, 1.8, 0.8);
    writeLn('BSquare =', b.BSquare():8:2);
    readLn;
end.

Простой полиморфизм

Простой полиморфизм – механизм переопределения методов при наследовании, при котором связь метода с объектом выполняется на этапе компиляции (раннее связывание).

program Ex_7_06;

{$APPTYPE CONSOLE}
uses
    SysUtils,
    Room in 'Ex_08_02\Room.pas';

type
    TVRoom2 = object(TRoom)
        height: Single;
        function square(): Single;
        procedure init(l, w, h: Single);
    end;

procedure TVRoom2.init;
begin
    inherited init(l, w);     { TRoom.init(l,w);}
    Height := h;
end;

function TVRoom2.square;
begin
    result := 2 * (inherited square() + height * (length + width));
end;

var
    a: TVRoom2;

begin
    a.init(3.4, 5.1, 2.8);
    writeLn('Square = ', a.square():6:2);
    readLn;
end.

Обращение объекта производного класса к переопределенному методу базового класса в программе

При необходимости обращении к переопределенному методу базового класса явно меняют тип переменной – объекта класса, например так

var
    a: TVRoom2;
    b: TRoom;

{...}
b := a;
b.square();

Сложный полиморфизм. Конструкторы

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

3 случая обязательного использования сложного полиморфизма

1-й случай – если наследуемый метод для объекта производного класса вызывает метод, переопределенный в производном классе.

2-й случай – если объект производного класса через указатель базового класса обращается к методу, переопределенному производным классом.

3-й случай – если процедура вызывает переопределенный метод для объекта производного класса, переданного в процедуру через параметр-переменную, описанный как объект базового класса («процедура с полиморфным объектом»).

1-й случай

program Ex_7_07;

{$APPTYPE CONSOLE}
uses
    SysUtils;

// ================ TRoomP ================
type
    TRoomP = object
        length, width: Single;
        function square: Single;
        procedure print();
        procedure init(l, w: Single);
    end;

function TRoomP.square;
begin
    result := length * width;
end;

procedure TRoomP.print;
begin
    writeln('Square = ', square():6:2);
end;

procedure TRoomP.init;
begin
    length := l;
    width := w;
end;

// ================ TVRoomP ================
type
    TVRoomP = object(TRoomP)
        height: Single;
        function square: Single;
        procedure init(l, w, h: Single);
    end;

procedure TVRoomP.init;
begin
    inherited init(l, w);
    height := h;
end;

function TVRoomP.square;
begin
    square := 2 * (inherited square() + height * (length + width));
end;

// ================  ================
var
    a: TRoomP;
    b: TVRoomP;
begin
    a.init(3.5, 5.1);
    a.print();               // `Square =  17.85` -- верно
    
    b.init(3.5, 5.1, 2.7);
    b.print();               // `Square =  17.85` -- неверно, должно быть: `82.14`
    writeLn(b.square():6:2); // `82.14`           -- верно
    readln;
end.
Пояснение к ошибке

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

Реализация сложного полиморфизма

Для организации сложного полиморфизма необходимо:

  1. переопределяемые методы описать служебным словом virtual;
  2. к методам класса с виртуальными полиморфными методами добавить специальный метод-процедуру – конструктор, в котором служебное слово procedure заменено служебным словом constructor;
  3. вызвать конструктор прежде, чем произойдет первое обращение к виртуальным полиморфным методам.

Подключение осуществляется с использованием таблицы виртуальных методов (ТВМ), которая создается при выполнении конструктора.

tbm

Различие раннего и позднего связывания

Раннее связывание – адрес метода определяется на этапе компиляции по объявленному типу переменной.

early

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

lately

Исправленный пример
unit RoomP;

interface
    type
        TRoomP = object
            length, sidth: Single;
            function square(): Single; virtual;    // !!!
            procedure print();
            constructor init(l, w: Single);
        end;

    type
        TVRoomP = object(TRoomP)
            height: Single;
            function square: Single; virtual;      // !!!
            constructor init(l, w, h: Single);
        end;

implementation
    function TRoomP.square;
    begin
        result := length * width;
    end;

    procedure TRoomP.print;
    begin
        writeln('Square = ', square():6:2);
    end;

    constructor TRoomP.init;
    begin
        length := l;
        width := w;
    end;

    constructor TVRoomP.init;
    begin
        inherited init(l, w);
        height := h;
    end;

    function TVRoomP.square;
    begin
        square := 2 * (inherited square() + height * (length + width));
    end;

end.
program Ex_7_07a;

{$APPTYPE CONSOLE}
uses
    SysUtils,
    RoomP in 'RoomP.pas';

var
    a: TRoomP;
    b: TVRoomP;
begin
    a.init(3.5, 5.1);
    a.print();                // `Square = 17.85`
    b.init(3.5, 5.1, 2.7);
    b.print();                // `Square =  82.14`
    readln;
end.

2-й случай

program Ex_7_07b;

{$APPTYPE CONSOLE}
uses
    SysUtils,
    RoomP in 'Ex_07_07\RoomP.pas';

var
    pA: ^TRoomP;
    B: TVRoomP;
begin
    B.init(3.5, 5.1, 2.7);
    writeLn('Square =', B.Square:6:2);    // `Square = 82.14`
    pA := @B;
    writeLn('Square =', pA^.Square:6:2);  // `Square = 82.14`
    readLn;
end.

3-й случай

program Ex_7_07c;

{$APPTYPE CONSOLE}
uses
    SysUtils,
    RoomP in 'Ex_08_07\RoomP.pas';

procedure print(var r: TRoomP);
begin
    writeln('Square =', r.square:6:2);
end;

var
    a: TRoomP;
    b: TVRoomP;

begin
    a.init(3.5, 5.1);
    b.init(3.5, 5.1, 2.7);
    print(a);
    print(b);
    readln;
end.

Функция определения типа полиморфного объекта

typeOf(<имя_класса_или_объекта>): pointer – возвращает адрес ТВМ класса или класса объекта. Если адрес ТВМ объекта и класса совпадают, то объект является переменной данного класса.

if typeOf(self) = typeOf(<имя_класса>) then
    {Объект принадлежит класс}>
else
    {Объект не принадлежит классу}

Свойства виртуальных методов класса

  1. позднее связывание требует построения ТВМ, а следовательно больше памяти;
  2. вызов виртуальных полиморфных методов происходит через ТВМ, а следовательно медленнее;
  3. список параметров одноименных виртуальных полиморфных методов должен совпадать, а статических полиморфных – не обязательно;
  4. статический полиморфный метод не может переопределить виртуальный полиморфный метод.

Динамические полиморфные объекты. Деструкторы

Создание полиморфных объектов

Функция new(<тип_указателя>) – возвращает адрес размещенного и, возможно, сконструированного объекта. После необходим вызов конструктора.
Или new(<тип_указателя>, <конструктор>).

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

Уничтожение полиморфных объектов

Процедура dispose(<указатель>) – освобождает память, на которую указывает указатель. Перед вызовом процедуры необходим вызов деструктора, если он указан, и затем – выполняется освобождение памяти.
Или dispose(<указатель>, <деконструктор>).

program Ex_7_08;

{$APPTYPE CONSOLE}
uses
    SysUtils;

type
    pTRoomD = ^TRoomD;

    TRoomD = object
        length, width: Single;
        function square(): Single; virtual;
        constructor init(l, w: Single);
        destructor done;
    end;

type
    pTVRoomD = ^TVRoomD;
    TVRoomD = object(TRoomD)
        height: Single;
        function square(): Single; virtual;
        constructor init(l, w, h: Single);
    end;

function TRoomD.square;
begin
    result := length * width;
end;

constructor TRoomD.init;
begin
    length := l;
    width := w;
end;

destructor TRoomD.done;
begin
end;

constructor TVRoomD.init;
begin
    inherited init(l, w);
    height := h;
end;

function TVRoomD.square;
begin
    result := 2 * (inherited square() + height * (length + width));
end;

var
    pA: pTRoomD;
    pB: pTVRoomD;

begin
    {указатель базового типа, объект базового типа}
    pA := new(pTRoomD);
    pA^.init(3.5, 5.1);
    writeLn('Square =', pA^.square():6:2);  // `Square = 17.85`
    pA^.done();
    dispose(pA);

    {указатель производного типа, объект производного типа}
    pB := new(pTVRoomD);
    pB^.init(3.5, 5.1, 2.7);
    writeLn('Square =', pB^.square():6:2);  // `Square = 82.14`
    Dispose(pB, done);

    {указатель базового типа, объект производного типа}
    pA := new(pTVRoomD, init(3.5, 5.1, 2.7));
    writeLn('Square =', pA^.square():6:2);  // `Square = 82.14`
    dispose(pA, done);

    readLn;
end.

Динамические поля в объектах

program Ex_7_09;

{$APPTYPE CONSOLE}
uses
    SysUtils;

type
    pTRoomD = ^TRoomD;

    TRoomD = object
        length, width: Single;
        function square(): Single; virtual;
        constructor init(l, w: Single);
        destructor done; virtual;
    end;

type
    pTBRoomD = ^TBRoomD;

    TBRoomD = object(TRoomD)
        pB: pTRoomD;
        function square: Single; virtual;
        function BSquare: Single;
        constructor init(l, w: Single; lb, wb: Single);
        destructor done; virtual;
    end;

function TRoomD.square;
begin
    square := length * width;
end;

constructor TRoomD.init;
begin
    length := l;
    width := w;
end;

destructor TRoomD.done;
begin
end;

constructor TBRoomD.init;
begin
    inherited init(l, w);
    if (lb = 0) or (wb = 0) then
        pB := nil
    else
        pB := new(pTRoomD, init(lb, wb));
end;

function TBRoomD.BSquare;
begin
    if pB <> nil then
        BSquare := pB^.square
    else
        BSquare := 0;
end;

function TBRoomD.square;
begin
    square := inherited square() + BSquare();
end;

destructor TBRoomD.done;
begin
    if pB <> nil then
        Dispose(pB, done);
end;

var
    A: TBRoomD;
    pB1: pTBRoomD;
    pB2: pTRoomD;

begin
    {статический объект с динамическим полем}
    A.init(3.2, 5.1, 2.5, 1);
    writeLn(A.Square():6:2, A.BSquare():6:2);                // ` 18.82  2.50`
    A.done;
    
    {динамический полиморфный объект с динамическим полем}
    pB1 := new(pTBRoomD, init(3.2, 5.1, 2.5, 1));
    writeLn(pB1^.square():6:2, pB1^.BSquare():6:2);          // ` 18.82  2.50`
    dispose(pB1, Done);
    
    {динамический полиморфный объект с динамическим полем}
    pB2 := new(pTBRoomD, init(3.2, 5.1, 2.5, 1));
    writeLn(pB2^.square:6:2, pTBRoomD(pB2)^.BSquare():6:2);  // ` 18.82  2.50`
    dispose(pB2, done);
    
    readLn;
end.

Технология событийного программирования. События Windows, сообщения и события Delphi. Основные события Delphi. Примеры.

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

События Delphi и их обработчики

Обработчики сообщений Windows предусмотрены у объектов класса TForm и классов управляющих компонентов, таких как кнопки, редакторы и т. п.

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

  1. В системе происходит событие, например, пользователь передвинул мышь или нажал на клавишу клавиатуры, в результате генерируется сообщение об этом событии – сообщение Windows.
  2. Сообщение Windows диспетчируется конкретному приложению.
  3. В приложении сообщение передается активному компоненту (окну или управляющему элементу).
  4. Метод обработки сообщения Windows компонента инициирует заранее предусмотренные события Delphi.
  5. Если в приложении предусмотрен соответствующий обработчик события Delphi, то он вызывается, если нет – то продолжается обработка сообщения Windows.

События Delphi

Обработчики сообщений Windows, встроенные в классы компонентов VCL, инициируют множество событий Delphi.

Например, обработчик события клавиатуры WM_CHAR класса TForm инициирует три события Delphi.

enter image description here

Обработчики событий Delphi

Для каждого обработчика событий предусмотрен заголовок и определённый список передаваемых ему параметров.
procedure <компонент>.<Имя_компонента><Имя_события_delphi>(Sender: TObject[, {...}]);

  1. procedure TForm1.FormActivate(Sender:TObject);
    Параметр Sender – отправитель (инициатор события).
  2. procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
    Параметр Key – символ ANSI.
  3. procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    Параметры: Key – виртуальный код, Shift – управляющие клав.
  4. procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);

Примеры событий Delphi, обрабатываемые объектами класса TForm

  1. при изменении состояния формы:

    • OnCreate – в начальной стадии создания формы - используется при необходимости задания параметров (цвет или размер);
    • OnActivate – при получении формой фокуса ввода (окно становится активным и ему адресуется весь ввод с клавиатуры);
    • OnShow – когда форма (окно) становится видимой;
    • OnPaint – при необходимости нарисовать или перерисовать форму;
    • OnResize - при изменении размеров формы на экране;
    • OnDeactivate – при потере формой фокуса ввода (окно становится неактивным);
    • OnHide – при удалении формы с экрана (окно становится невидимым);
    • OnCloseQuery – при попытке закрыть форму - обычно используется для создания запроса-подтверждения необходимости закрытия окна;
    • OnClose – при закрытии формы;
    • OnDestroy – при уничтожении формы;
  2. от клавиатуры и мыши:

    • OnKeyPressed – при нажатии клавиш, которым соответствует код ANSI;
    • OnKeyDown, OnKeyUp – при нажатии и отпускании любых клавиш;
    • OnClick, OnDblClick – при обычном и двойном нажатии клавиш мыши;
    • OnMouseMove – при перемещении мыши (многократно);
    • OnMouseDown, OnMouseUp – при нажатии и отпускании клавиш мыши;
  3. при перетаскивании объекта мышью:

    • OnDragDrop – в момент опускания объекта на форму;
    • OnDragOver – в процессе перетаскивания объекта над формой (многократно);
  4. другие:

    • OnHelp – при вызове подсказки;
    • OnChange – при изменении содержимого компонент.


Ещё полезные вещи

Некоторые опции/директивы компилятора

Каждая из них может быть включена директивой {$@+} или {$<dir> On} и отключена директивой {$@-} или {$<dir> Off} , где @ – одна из допустимых букв, <dir> – полное имя директивы (см. таблицу)

Краткая форма Полная форма По умолчанию* Описание
{$X} {$ExtendedSyntax} + Если включено можно: использовать result в функциях для возврата значений; вызывать функции как процедуры (никуда не возвращая результат); Обрабатывать массив char'ов как строки
{$I} {$IOChecks} + Если включено: проверяет правильность завершения операций ввода-вывода, если ошибка – вызывает ошибку выполнения программы; иначе – функция IOResult(): Word возвращает код статуса последней операции ввода-вывода
{$H} {$LongStrings} + Если включено: тип string присваивается к AnsiString, иначе к ShortString. (Хотя, кажется, с Delphi 2009 string всегда считается UnicodeString)
{$Q}** {$OverFlowChecks}** - Если включено: проверяет наличие переполнения при выполнении некоторых арифметических операций. При переполнении программа аварийно завершается
{$R}** {$RangeChecks}** - Если включено: проверяет правильность вычисленных индексов массивов. При выходе за рамки программа аварийно завершается

* – в Delphi Pascal 2009
** – являются отладочными директивами, поэтому с ними выполнение программы потребует больше времени и/или памяти

Источник: delphibasics.co.uk

Written with StackEdit