|
|
(не показано 67 промежуточных версий 7 участников) |
Строка 1: |
Строка 1: |
− | ==Подпрограммы== | + | [[Категория:Основы программирования]] |
− | ===Вспомогательные алгоритмы=== | + | [[Страница курса Основы программирования|К основной странице курса]] |
− | '''Вспомогательный алгоритм''' — это алгоритм, который используется для реализации другого алгоритма. | + | == Основные операторы == |
| + | === Оператор присваивания := === |
| + | <xh4> Синтаксис </xh4> |
| + | <переменная> ''':=''' <выражение> |
| | | |
− | Вспомогательные алгоритмы имеют:
| + | ''<u>Пример</u> использования оператора присваивания.'' |
− | *'''''имя'''''
| + | <source lang="Pascal"> |
− | *'''''список параметров''''' | + | a := (3 + 5) * 8; |
− | :некоторые параметры являются ''входными'', а некоторые — ''выходными'' | + | b := a + 2; |
| + | </source> |
| | | |
− | Если один из ''выходных'' параметров возвращается особым образом, так что алгоритм можно использовать в выражении, то такой алгоритм называется '''алгоритмом-функцией'''.
| + | <xh4> Семанитика </xh4> |
| + | Вычисляется выражение в правой части, при этом, вместо имен переменных подставляются их значения. <br /> |
| + | Затем результат вычисления записывается в переменную в левой части. |
| | | |
− | В программировании вспомогательные алгоритмы называются '''подпрограммами''' и делятся на:
| + | '''Ограничение.''' Тип выражения должен быть '''''совместим по присваиванию''''' с переменной. <br /> |
− | *'''''процедуры'''''
| + | Например: |
− | *'''''функции''''' | + | * одинаковые типы совместимы. |
| + | * выражение типа <tt>integer</tt> можно присвоить переменной типа <tt>real</tt>. Обратное неверно. |
| | | |
| + | <xh4> Операторы присваивания += и *= </xh4> |
| + | ''<u>Пример</u>.'' |
| + | <source lang="Pascal"> |
| + | d += 1; //прибавить 1 к d |
| + | d *= 2; //умножить d на 2 |
| + | </source> |
| | | |
− | '''Важно.''' Подпрограммы позволяют избежать дублирования кода при решении однотипных задач.
| + | <xh4> Примеры использования := </xh4> |
− | Алгоритм один раз описывается, а затем может быть многократно вызван с различным набором параметров из другого алгоритма.
| + | ''<u>Пример 1</u>.'' Перемена местами двух значений. |
− | | + | Дано: <tt>x, y</tt>; |
− | <small>'''Лекция 9'''</small> | + | <source lang="Pascal"> |
− | | + | var x, y: integer; |
− | ===Процедуры===
| |
− | '''Пример.'''
| |
− | :Даны 3 пары положительных чисел. | |
− | :Найти их среднее арифметическое и среднее геометрическое.
| |
− | Очевидно, удобно сделать подпрограмму, которой бы на вход подавались два числа, а выходными параметрами являлись их среднее арифметическое и среднее геометрическое соответственно.
| |
− | <source lang="Pascal">procedure CalcSrASrG(a, b: real; var SA, SG: real);
| |
− | begin
| |
− | SA := (a + b) / 2;
| |
− | SG := sqrt(a * b);
| |
− | end;</source>
| |
− | ''Строка'' | |
− | <source lang="Pascal">procedure CalcSrASrG(a, b: real; var SA, SG: real); </source> | |
− | называется '''заголовком процедуры'''.
| |
− | | |
− | Теперь можем многократно использовать описанную процедуру в основной программе:
| |
− | <source lang="Pascal">begin //основной программы
| |
− | for var i:=1 to 3 do
| |
− | begin
| |
− | var a, b: real;
| |
− | readln(a, b);
| |
− | var SA, SG: real;
| |
− | CalcSrASrG(a, b, SA, SG);
| |
− | writeln(SA, '|', SG);
| |
− | end;
| |
− | end.</source>
| |
− | ''Оператор''
| |
− | <source lang="Pascal">CalcSrASrG(a, b, SA, SG);</source>
| |
− | называется '''оператором вызова процедуры'''.
| |
− | | |
− | <xh4>Синтаксис описания процедуры</xh4>
| |
− | '''procedure''' <имя> [( <список формальных параметров> )];
| |
− | <раздел описаний>
| |
− | '''begin'''
| |
− | <операторы и внутриблочные переменные>
| |
− | '''end;'''
| |
− | '''Замечание.''' Pascal допускает ''внутренне описание'' п/п (''вложенность'' процедур)
| |
− | <список формальных пар-ов> ::= <секция ф.п.> | <секция ф.п.>; <список ф.п.>
| |
− | <секция ф.п.> ::= ['''var'''] <список имен>: <тип>
| |
− | <xh4>Синтаксис вызова процедуры</xh4>
| |
− | <имя процедуры> [( [<список фактических параметров>] )] | |
− | | |
− | <список фактических пар-ов> ::= <список выражений>
| |
− | (<список выражений> ::= <выражение>{, <выражение>})
| |
− | <xh4>Семантика вызова процедуры</xh4>
| |
− | *имя должно быть именем процедуры, описанной ранее (или именем текущей процедуры)
| |
− | *количество фактических параметров должно совпадать с количеством формальных параметров
| |
− | *фактические '''''параметры-переменные''''' должны быть именами переменных, типы которых совпадают с типами соответствующих формальных параметров
| |
− | *фактические '''''параметры-значения''''' должны быть выражениями, типы которых совместимы по присваиванию с типами соответствующих формальных параметров
| |
− | | |
− | ===Входно-выходные параметры===
| |
− | Параметр x называется '''входно-выходным''', если его значение необходимо в начале алгоритма, оно модифицируется алгоритмом и возвращается в вызывающую программу.
| |
− | | |
− | ''Входно-выходные'' параметры описываются с ключевым словом '''var''', как и ''выходные''.
| |
− | | |
− | '''Пример 1.''' Увеличение значения переменной на заданное число.
| |
− | <source lang="Pascal">procedure Inc(var x: integer; n: integer);
| |
− | begin
| |
− | x += n;
| |
− | end;</source>
| |
− | Вызов процедуры:
| |
− | <source lang="Pascal">var x: integer; //неинициализированная переменная | |
− | x := 1; //x инициализировано
| |
− | Inc(x, 2);</source>
| |
− | | |
− | '''Пример 2.''' Перемена местами значений двух переменных.
| |
− | <source lang="Pascal">procedure Swap(var x,y: integer);
| |
| begin | | begin |
| + | read(x,y); |
| var v := x; | | var v := x; |
| x := y; | | x := y; |
| y := v; | | y := v; |
− | end; | + | writeln(x, ' ', y); |
| + | end. |
| + | </source> |
| | | |
− | begin
| + | Это стандартное решение. |
− | var a := 5;
| + | В PascalABC.NET на основе этого алгоритма определена стандартная процедура <tt>'''Swap'''(x, y)</tt>. |
− | var b := 3;
| |
− | Swap(a,b);
| |
− | end.</source>
| |
| | | |
− | ===Функции===
| + | Однако, существуют и другие решения. Например: |
− | '''Функции''' являются др. разновидностью подпрограмм. Это п/п, возвращающая одно значение особым образом так, что ее вызов можно использовать в выражении.
| + | <source lang="Pascal"> |
− | Точнее, — вызов процедуры является оператором, а вызов функции — выражением.
| + | var x, y: integer; |
− | | |
− | '''Пример'''. Функция sgn(x) — знак числа
| |
− | <source lang="Pascal">function Sign(x: real): integer; | |
| begin | | begin |
− | if x > 0 then | + | read(x, y); |
− | Result := 1
| + | x := x + y; |
− | else if x < 0 then | + | y := x - y; |
− | Result := -1
| + | x := x - y; |
− | else | + | writeln (x, ' ', y); |
− | Result := 0;
| + | end. |
− | end;</source> | + | </source> |
| | | |
− | Строка <source lang="Pascal">function Sign(x: real): integer;</source>
| + | ''<u>Пример 2</u>.'' Использование промежуточных переменных в вычислениях |
− | называется '''заголовком функции''', а <tt>integer</tt> — это '''тип возвращаемого значения'''.
| + | Дано: <tt>x: real</tt>; |
| + | Найти: <tt>x<sup>15</sup></tt>; |
| | | |
− | В каждой функции неявно определена переменная <tt>'''result'''</tt>, хранящая возвращаемое значение функции и имеющая тот же тип.
| + | <u>Решение 1</u>. |
− | Присваивание значения Result в теле функции ''обязательно по всем ветвям'' алгоритма.
| + | <source lang="Pascal"> |
| + | y := x * x; |
| + | z := y * y; |
| + | t := z * z; |
| + | p := t * z; |
| + | q := p * x * y; |
| + | </source> |
| | | |
− | '''Пример ''неправильной (!!!)''''' функции.
| + | <u>Решение 2</u>. |
− | <source lang="Pascal">function f(x: integer): integer; | + | <source lang="Pascal"> |
− | begin
| + | y := x * x; |
− | if x > 0 then
| + | z := y * x; |
− | Result := 777;
| + | t := z * y; |
− | end;
| + | p := t * t * t; |
| + | </source> |
| | | |
− | begin
| + | <u>Решение 3</u>. |
− | writeln(f(0));
| + | <source lang="Pascal"> |
− | end.</source>
| + | y := x * x; |
− | Выведется непредсказуемое значение, находящееся в этот момент в памяти, т.к. по ветке x = 0 переменная Result не инициализируется.
| + | x := x * y * y; |
− | | + | t := x * x * x; |
− | '''Пример.''' Считывание значения с клавиатуры.
| + | </source> |
− | <source lang="Pascal">function ReadInteger: integer;
| |
− | begin
| |
− | read(Result);
| |
− | end;</source>
| |
− | '''''Сравнение использования процедуры и функции'''''
| |
− | процедура функция
| |
− | var x: integer; var x := ReadInteger();
| |
− | read(x);
| |
− | '''Пример.''' Сумма первых N натуральных чисел.
| |
− | <source lang="Pascal">function SumN(N: integer): integer; | |
− | begin
| |
− | Result := 0;
| |
− | for var i:=1 to N do
| |
− | Result += i;
| |
− | end;</source>
| |
− | <xh4>Синтаксис описания функции</xh4>
| |
− | '''function''' <имя>[( [<список формальных параметров>] )]: <тип возвращаемого значения>;
| |
− | <раздел описаний>
| |
− | '''begin'''
| |
− | <операторы и внутриблочные описания>
| |
− | '''end''';
| |
− | <xh4>Синтаксис вызова функции</xh4>
| |
− | <имя>[( [<список формальных параметров>] )];
| |
− | <xh4>Семантика</xh4>
| |
− | '''Замечание.''' Формальные параметры функции ''не'' рекомендуется делать '''параметрами-переменными'''.
| |
− | | |
− | Считается, что функция должна вычислять и возвращаеть результат, и более ничего не делать.
| |
− | Если функция возвращает результат и выполняет дополнительное действие, которое сказывается вне функции, то такие действия называются '''побочным рузультатом вызова функции.'''
| |
− | | |
− | '''Правило.'''
| |
− | :В подавляющем большинстве случаев функция не должна иметь побочный эффект.
| |
− | | |
− | ===Оператор exit===
| |
− | Оператор '''<tt>exit</tt>''' — оператор досрочного завершения подпрограммы.
| |
− | | |
− | '''Пример'''. Поиск числа k среди n введенных.
| |
− | <source lang="Pascal">function Find(n: integer; k: integer): boolean;
| |
− | begin
| |
− | for var i:=1 to n do
| |
− | begin
| |
− | var x := ReadInteger;
| |
− | if x = k then
| |
− | begin
| |
− | result := true;
| |
− | exit;
| |
− | end;
| |
− | end;
| |
− | result := false;
| |
− | end;</source>
| |
− | '''Замечание.''' Оператор <tt>'''exit'''</tt>, в отличие от <tt>'''break'''</tt>, позволяет завершить подпрограмму и выйти сразу из нескольких вложенных циклов. При вызове его в теле основной программы программа завершается.
| |
− | | |
− | ==Локальные и глобальные переменные==
| |
− | <source lang="Pascal">var
| |
− | n: integer;
| |
− | m: integer;
| |
− | | |
− | function f(x: integer): integer;
| |
− | var n: integer;
| |
− | begin
| |
− | writeln(n);
| |
− | writeln(m);
| |
− | n := 5;
| |
− | end;</source>
| |
− | '''Замечание.''' '''Локальная''' переменная, описанная в процедуре или функции, может иметь то же имя, что и '''глобальная''' переменная.
| |
− | Если глобальная описана ранее, то её имя внутри процедуры становится недоступным.
| |
− | | |
− | Глобальные переменныя по умолчанию инициализируются '''0''', а локальные по умолчанию не инициализируются.
| |
− | Локальная переменная <tt>'''Result'''</tt> в функции по умолчанию также не инициализируется.
| |
− | | |
− | В данном примере:
| |
− | :writeln(n) выведет непредсказуемый результат;
| |
− | :writeln(m) выведет 0;
| |
− | | |
− | ==Пространство имен==
| |
− | Область программы, в которой не может быть описано двух объектов с одинаковыми именем, называется '''пространством имен'''.
| |
− | | |
− | Различают:
| |
− | *'''''глобальное''''' пространство имен | |
− | *пространство имен '''''каждой п/п''''' | |
− | | |
− | ''Формальные'' параметры п/п являются ''локальными'' по отношению к эти п/п.
| |
− | | |
− | '''Примеры''' допустимых и недопустимых описаний:
| |
− | '''procedure''' p(p: integer); //Можно
| |
− |
| |
− | '''function''' Result: integer; //Можно
| |
− |
| |
− | '''procedure''' p(i: integer); //НЕЛЬЗЯ
| |
− | '''var''' i: integer;
| |
− |
| |
− | function f(Result: real): real; //НЕЛЬЗЯ
| |
− | <small>'''Лекция 10'''</small>
| |
− | ==Область видимости и время жизни объекта==
| |
− | '''Время жизни переменной''' — время, в течение которого существует отведенная ей память.
| |
− | *для глобальных переменных это время работы программы
| |
− | *для локальных — с момента описания до конца работы блока, где они описаны
| |
| | | |
− | '''Область видимости переменной''' — область в программе, в которой можно обратиться к переменной по имени. | + | Заметим, что в первом решении используется 6 операций умножения, в во 2м и 3м — 5. Возникает задача: '''''найти x<sup>n</sup> за минимальное число умножений.''''' <br /> |
− | ==Побочный эффект==
| + | Об этом читай [http://it.mmcs.sfedu.ru/forum?func=view&id=22350&catid=1 тему]. |
− | '''Побочным эффктом подпрограммы''' называется также любое изменение глобальных переменных (или параметров для функции). | |
| | | |
− | '''Пример.''' | + | === Оператор ввода === |
− | <source lang="Pascal">var n: integer; | + | <xh4> Синтаксис </xh4> |
− | procedure p;
| + | '''read''' (<список переменных>) | '''readln''' (<список переменных>) |
− | begin
| |
− | n += 1;
| |
− | end;
| |
| | | |
− | begin
| + | <xh4> Семантика </xh4> |
− | n := 5;
| + | Происходит считывание данных с клавиатуры и запись их в переменные из <tt><списка переменных></tt>. |
− | p;
| + | Вводить данные нужно либо через пробел, либо по нажатию <tt><Enter></tt>, при этом программа не перейдет к выполнению следующего оператора, пока не будут считаны все данные. |
− | write(p);
| |
− | end.</source>
| |
| | | |
− | '''Замечание.''' Если подпрограмма желает изменить глобальную переменную, то в большинстве случаев эту переменную следует передать как параметр.
| + | Имеются также стандартные функции ReadInteger, ReadReal, ReadlnInteger, ReadlnReal: |
| | | |
− | ==Принцип локальности==
| + | <source lang="Delphi">var n := ReadInteger; |
− | Рассмотрим пример:
| + | var r := ReadlnReal;</source> |
− | <source lang="Pascal">var n: integer; | |
| | | |
− | procedure p;
| + | С процедурой ввода связан ряд '''''ошибок''''' времени выполнения (например, если переменная используется в качестве делителя, и вводится 0, или, если должно быть получено целое число, а вводится <tt>'ABC'</tt>). Эти ошибки нужно уметь обрабатывать. |
− | begin
| |
− | <много кода; n не используется>
| |
− | end;
| |
| | | |
− | procedure p1;
| + | === Оператор try/except и обработка ошибок ввода === |
− | begin
| + | Операторы, которые могут получать ошибку, заключаются специальный охранный блок - оператор <tt>'''try'''</tt>. |
− | <еще больше кода; n не используется>
| |
− | end;
| |
| | | |
− | begin
| + | <xh4> Синтаксис </xh4> |
− | <10 операторов>
| |
− | n := 1;
| |
− | <еще операторы>
| |
− | end.</source>
| |
− | '''Принцип локальности''' — эмпирический принцип, по которому переменную следует описывать и впервые инициализировать в том месте алгоритма, где она начинает впервые использоваться.
| |
− | | |
− | Обычно входные и выходные данные описываются, как глобальные переменные, после всех подпрограмм (если эти подпрограммы их не используют).
| |
− | В начале текста программы обычно используются переменные и константы, инициализируемые каким-то значением, которое легко поменять при следующих запусках этой подпрограммы.
| |
− | | |
− | '''Пример.''' Вычислить произведениe целых чисел от a до b.
| |
− | '''Решение 1.'''
| |
− | <source lang="Pascal">var
| |
− | p := 1;
| |
− | a, b: integer;
| |
− | i: integer;
| |
− | begin
| |
− | read(a, b);
| |
− | for i:=a to b do
| |
− | p *= i;
| |
− | writeln(p);
| |
− | writeln(i); //от компилятора зависит, будет i = b или (b + 1)
| |
− | end.</source>
| |
− | Внесем изменения:
| |
− | *опишем i в заголовке цикла
| |
− | :при описании переменной в заголовке цикла её область видимости и время жизни ограничены телом цикла
| |
− | *инициализируем p непосредственно перед циклом, вычисляющим произведение
| |
− | '''Решение 2.'''
| |
− | <source lang="Pascal">var
| |
− | p: integer;
| |
− | a, b: integer;
| |
− | begin
| |
− | read(a, b);
| |
− | p := 1;
| |
− | for var i:=a to b do
| |
− | p *= i;
| |
− | writeln(p);
| |
− | end.</source>
| |
− | Второе решение является более ''масштабируемым'', чем первое, поскольку алгоритм вычисления произведения расположен непрерывно в тексте программы и его можно легко превратить в подпрограмму.
| |
− | | |
− | ==Внутренний механизм передачи параметра-переменной==
| |
− | <source lang="Pascal">procedure MyInc(var a: real; n: integer);
| |
− | begin
| |
− | a += n;
| |
− | end;
| |
− | | |
− | var
| |
− | b: real;
| |
− | i: integer;
| |
− | begin
| |
− | b := ReadReal;
| |
− | i := ReadInteger;
| |
− | MyInc(b, i);
| |
− | writeln(b);
| |
− | end.</source>
| |
− | Здесь
| |
− | :b, i: фактические параметры при вызове MyInc
| |
− | :a, n: формальные параметры (a — параметр-переменная; n — параметр-значение)
| |
− | | |
− | При передаче параметра-значения i в соответствующий формальный параметр копируется '''значение i'''.
| |
− | А вот механизм передачи параметра-переменной называется '''передачей по ссылке'''.
| |
− | | |
− | '''Ссылкой''' обычно называют другое имя некоторого объекта.
| |
− | | |
− | Для параметра-переменной имя соответствующего формального параметра выступает в роли ссылки переменной-фактического параметра.
| |
− | Все, что происходит со ''ссылкой'' внутри подпрограммы происходит и с ''самим объектом'' вне подпрограммы, поскольку это один и тот же объект.
| |
− | | |
− | При передаче параметра ''по ссылке'' в подпрограмму передается не сама переменная, а её '''адрес''':
| |
− | MyInc(var a: real; i: integer);
| |
− | |
| |
− | '''@'''b ('''адрес''' b)
| |
− | '''Замечание 1.''' В 32-битных ОС размер адреса — 4 байта, это позволяет адресовать примерно 4 Гб операционной памяти.
| |
− | <br>'''Замечание 2.''' Размер адреса не зависит от размера переменной, поэтому при передаче параметров большого размера их предпочитают передавать по ссылке.
| |
− | | |
− | ==Перегрузка имен подпрограмм==
| |
− | Предположим, у нас описана процедура:
| |
− | <source lang="Pascal">procedure Swap(a, b: integer);
| |
− | begin
| |
− | var t := a;
| |
− | a := b;
| |
− | b := t;
| |
− | end;</source>
| |
− | Возможно ли описать процедуру с таким же именем, параметры которой были бы вещественными? Да, возможно:
| |
− | <source lang="Pascal">procedure Swap(a, b: integer);
| |
− | begin
| |
− | var t := a;
| |
− | a := b;
| |
− | b := t;
| |
− | end;
| |
− | | |
− | procedure Swap(a, b: real);
| |
− | begin
| |
− | var t := a;
| |
− | a := b;
| |
− | b := t;
| |
− | end;</source>
| |
− | Таким образом, в одном пространстве имен можно описать несколько подпрограмм с одним именем, но разными типами или количеством формальных параметров.
| |
− | <br>При вызове такой подпрограммы на ''этапе компиляции'' типы фактических параметров сравниваются с типами формальных для всех версий этой подпрограммы и выбирается наиболее подходящая.
| |
− | | |
− | '''Замечание 1.''' Тип возвращаемого значения функции не участвует в операции разрешения перегрузки.
| |
− | | |
− | Версии подпрограммы с одним именем называются '''перегруженными''', а определение того, какую версию выбрать — '''разрешением перегрузки'''.
| |
− | | |
− | '''Замечание 2.''' Разрешить перегрузку можно не всегда.
| |
− | <br>'''Пример.'''
| |
− | <source lang="Pascal">procedure p(i: integer; r: real);
| |
− | ...
| |
− | | |
− | procedure p(r: real; c: char);
| |
− | ...
| |
− | | |
− | procedure p(r: real; i: integer);
| |
− | ...
| |
− | | |
− | begin
| |
− | p(1, 2); //неоднозначность
| |
− | end.</source>
| |
− | '''Замечание 3.''' В процессе разрешения перегрузки происходит ''неявное преобразование типов''.
| |
− | <small>'''Лекция 11'''</small>
| |
− | | |
− | ==Параметры по умолчанию==
| |
− | <source lang="Pascal">procedure DoOp(a, b: integer; var res: integer; op: char := '+');
| |
− | begin
| |
− | case op of
| |
− | '+': res := a + b;
| |
− | '-': res := a - b;
| |
− | '*': res := a * b;
| |
− | end;
| |
− | end;
| |
− | ...
| |
− | DoOp(1,2, res, '-'); // res = -1
| |
− | DoOp(3,4, res1); // res1 = 7</source>
| |
− | '''Параметры по умолчанию''' обязательно должны находиться последними в списке параметров. Если при вызове не указывать этот параметр, будет использовано значение по умолчанию.
| |
− | | |
− | '''Замечание.''' Параметры по умолчанию корректно сочетаются с перегрузкой.
| |
− | ==Предварительное объявление подпрограмм==
| |
− | <source lang="Pascal">procedure p;
| |
− | begin
| |
− | q;
| |
− | end;
| |
− | | |
− | procedure q;
| |
− | begin
| |
− | ...
| |
− | end;</source>
| |
− | Такое описание вызовет ошибку компиляции, т.к. в '''<tt>p</tt>''' используется еще не известная процедура. Чтобы таких ошибок не возникало, нужно использовать ''предварительное объявление подпрограммы'':
| |
− | <source lang="Pascal">procedure q; forward;
| |
− | | |
− | procedure p;
| |
− | begin
| |
− | q;
| |
− | end;
| |
− | | |
− | procedure q;
| |
− | ...</source>
| |
− | '''forward-объявление''' делается для подпрограмм, которые ''обязательно'' будут описаны ниже. Если программа не будет описана в этом же файле, то, в конце компиляции этого файла, мы получим ошибку компилятора о <tt>'''forward'''</tt>, который был объявлен, но не описан.
| |
− | | |
− | ==Процедурные переменные==
| |
− | ===Процедурный тип и переменные===
| |
− | <source lang="Pascal">var pr: procedure(a, b: real; var res: real);</source>
| |
− | Переменная называется '''процедурной''' если ей можно присваивать процедуры или функции указанного типа.
| |
| <source lang="Pascal"> | | <source lang="Pascal"> |
− | type
| + | try |
− | BinOpType = procedure(a, b: real; var res: real);
| |
− | | |
− | var
| |
− | pr: BinOpType;
| |
− | | |
− | procedure AddOp(a, b: real; var res: real);
| |
− | begin
| |
− | res := a + b;
| |
− | end;
| |
− | procedure MultOp(a, b: real; var res: real);
| |
− | begin
| |
− | res := a * b;
| |
− | end;
| |
− | | |
− | begin
| |
− | pr := AddOp;
| |
− | var r: real;
| |
− | pr(1,2, r); //вызов процедуры через процедурную переменную
| |
− | | |
− | pr := MultOp;
| |
− | pr(3,4, r);
| |
− | end.</source>
| |
− | После присваивания процедурной переменной имени процедуры, эта процедура может быть вызвана через переменную:
| |
− | <имя процедурной переменной>( <список параметров> );
| |
− | Такой способ вызова процедуры является чрезвычайно гибким, поскольку позволяет менять вызываемое действие в процессе работы программы.
| |
− | | |
− | '''Замечание.''' До первого присваивания процедурная переменная имеет тип <tt>'''nil'''</tt>. В этот момент вызывать процедуру через переменную нельзя — будет ошибка выполнения.
| |
− | | |
− | ===Функции обратного вызова (callback)===
| |
− | <source lang="Pascal">type ActionType = procedure;
| |
− | | |
− | procedure DoActions(Action1, Action2, Action3: ActionType);
| |
− | begin
| |
− | Action1;
| |
− | Action2;
| |
− | Action3;
| |
− | end;
| |
− | | |
− | procedure Eat;
| |
− | begin
| |
− | writeln('Студент ест');
| |
− | end;
| |
− | procedure Think;
| |
− | begin
| |
− | writeln('Студент думает');
| |
− | end;
| |
− | procedure Sleep;
| |
− | begin
| |
− | writeln('Студент спит');
| |
− | end;
| |
− | | |
− | begin
| |
− | DoActions(Sleep, Eat, Sleep); // Первый алгоритм подготовки к сессии
| |
− | DoActions(Think, Eat, Sleep); // Второй алгоритм подготовки к сессии
| |
− | DoActions(Sleep, Sleep, Think); // Третий алгоритм подготовки к сессии
| |
− | end.</source>
| |
− | Процедурные переменные могут являться параметрами других процедур.
| |
− | При вызове этих процедур им на вход в качестве параметров передаются имена процедур, котоые будут вызваны позже внутри основной процедуры.
| |
− | <br>Другими словами, мы передаем в процедуру действие, которое должно быть вызвано (будет вызвано) в определенный момент в этой процедуре.
| |
− | | |
− | Такой вызов называется '''обратным вызовом (callback)'''.
| |
− | <br>''Примечание.'' Прямой вызов — это передача процедуры в качестве параметра.
| |
− | | |
− | '''Пример.''' Вычисление определённого интеграла методом прямоугольников.
| |
− | <source lang="Pascal">type func = function(x: real): real;
| |
− | function Integral(a, b: real; N: integer; f: func);
| |
− | begin
| |
− | assert(a < b);
| |
− | var h := (b - a) / N;
| |
− | result := 0;
| |
− | var x := a;
| |
− | for var i:=0 to N-1 do
| |
− | begin
| |
− | result += f(x);
| |
− | x += h;
| |
− | end;
| |
− | result *= h;
| |
− | end;</source>
| |
− | | |
− | ===Вызов нескольких процедур через процедурную переменную===
| |
− | <source lang="Pascal">type ActionType = procedure;
| |
− | | |
− | procedure Eat;
| |
− | begin
| |
| ... | | ... |
− | end;
| + | readln(a); |
− | procedure Think;
| |
− | begin
| |
− | ... | |
− | end;
| |
− | procedure Sleep;
| |
− | begin
| |
| ... | | ... |
| + | except |
| + | <обработка ошибки> |
| end; | | end; |
| + | <продолжение работы> |
| + | </source> |
| | | |
− | var pr: ActionType;
| + | <xh4> Семантика </xh4> |
− | begin
| + | Если внутри блока '''<tt>try</tt>''' происходит ''ошибка выполнения'', то все последующие операторы в блоке игнорируются, и выполнение программы переходит к блоку '''<tt>except</tt>'''. По выходе из '''<tt>except</tt>''' программа продолжает работу. |
− | pr += Sleep;
| |
− | pr += Eat;
| |
− | pr += Think;
| |
− | pr;
| |
− | pr -= Eat;
| |
− | pr;
| |
− | end.</source>
| |
− | '''Замечание 1.''' Такое возможно только в .NET; | |
− | <br>'''Замечание 2.''' Подобный механизм не имеет смысла использовать для функций; | |
| | | |
− | <small>'''Лекция 12'''</small>
| + | Если ошибки не происходит, то выполняются все операторы в блоке '''<tt>try</tt>''', блок '''<tt>except</tt>''' не выполняется, и программа продолжает работу. |
− | ==Методы разработки подпрограмм==
| |
− | '''Пример.''' Вывести таблицу простых чисел <= 1000. | |
− | ===Метод разработки ''сверху вниз''===
| |
− | Метод разработки '''сверху вниз''' начинается с записи основного алгоритма в предположении,
| |
− | что все подпрограммы уже написаны, т.е. код пишется ''в крупных командах''.
| |
| | | |
− | '''Решение.''' | + | === Оператор вывода === |
− | <source lang="Pascal">writeln(2); | + | <xh4> Синтаксис </xh4> |
− | var x := 3;
| + | '''write'''(<список выражений>) | '''writeln'''(<список выражений>) |
− | while x <= 1000 do
| |
− | begin
| |
− | if IsPrime(x) then
| |
− | writeln(x);
| |
− | x += 2;
| |
− | end;</source>
| |
− | После этого реализуем функцию IsPrime;
| |
| | | |
− | Этот метод используется, когда:
| + | <xh4> Семантика </xh4> |
− | *основной алгоритм понят, и его можно сразу записать
| + | Выражения в списке вычисляются, и их значения выводятся на экран. <br /> |
− | *задача четко поставлена
| + | В случае <tt>writeln</tt> после вывода осуществляется переход на новую строку. |
− | *имеется ровно одна задача, которую необходимо решить
| |
| | | |
− | '''Замечание.''' В коде главной программы имеются вызовы других подпрограмм, которые реализуются позже (возможно, другими разработчиками); | + | <xh4> Форматы вывода </xh4> |
− | <br>На период, пока они не написаны, вместо них могут использоваться "'''заглушки'''" (подпрограммы с тривиальными телами) | + | После каждого выражения в списке вывода можно использовать '''формат вывода''' в виде <tt>''':a'''</tt>, где <tt>'''a'''</tt> — выражение целого типа.<br /> |
| + | После вещественного типа — <tt>''':a:b'''</tt> (<tt>'''a'''</tt> задает ширину поля вывода (выравнивание по правому краю), <tt>'''b'''</tt> — количество знаков в дробной части). |
| | | |
− | Код каждой разрабатываемой подпрограммы также может разрабатываться методом сверху вниз.
| + | <xh4> Вывод с помощью write[ln]Format </xh4> |
− | ===Метод разработки ''снизу вверх''===
| + | '''writelnFormat'''('<форматная строка>', <список выражений>) |
− | Вначале составляется "нижняя" подпрограмма, она всесторонне тестируется, и, потом,
| |
− | пишется основная программа или подпрограмма.
| |
| | | |
− | Метод используется, когда:
| + | ''<u>Пример</u> вывода с использованием форматной строки''. |
− | *на момент начала решения задача нечетко поставлена
| + | <source lang="Pascal"> |
− | *когда задача — большая | + | writelnFormat('{0} * {1} = {2}', a, b, a * b) |
− | *нет ясно выраженного главного алгоритма, а есть множество взаимосвязанных задач (примером может служить программа, реализующая деятельность университета)
| + | </source> |
| | | |
− | '''Замечание 1.''' Написание самых "нижних" подпрограмм позволяет лучше войти в предметную область;
| + | Будет выведено: |
− | <br>'''Замечание 2.''' С какого-то момента разработка хотя бы упрощенного главного алгоритма становится необходимой;
| + | a * b = '''a * b''' |
| | | |
− | Если между главным алгоритмом и "нижними" подпрограммами имеется несколько уровней,
| + | В форматной строке тоже можно использовать формат вывода. |
− | то имеет смысл '''сочетать оба метода разработки'''.
| + | <br><tt>{0, 10}</tt>: 10 — это ширина поля вывода |
− | ==Оператор goto==
| + | <br><tt>{0, 10:f3}</tt>: 3 — это количество знаков в дробной части для вещественного числа (показывает это спецификатор <tt>'''f'''</tt>). |
− | ===Синтаксис===
| + | <br><tt>{0, 10:e3}</tt> — экспоненциальный формат. |
− | '''goto''' <метка>
| |
− | ...
| |
− | <метка>: <оператор>
| |
− | '''Метка''' представляет собой идентификатор или целое положительное число.
| |
− | <br>Метка должна описываться в '''''разделе описаний меток''''' в виде: | |
− | '''label''' <метка1>, <метка2>...;
| |
− | === Семантические ограничения ===
| |
− | *метка должна использоваться в той же подпрограмме, в которой описана;
| |
− | *нельзя, используя goto, перейти внутрь какой-нибудь конструкции (цикла или условного оператора);
| |
− | <xh4>Пример плохого кода</xh4> | |
− | <source lang="Pascal">label 1, 2, 3;
| |
− | begin
| |
− | goto 1;
| |
− | <100 операторов>
| |
− | 2: write(2);
| |
− | goto 3;
| |
− | <100 операторов>
| |
− | 1: write(1);
| |
− | goto 2;
| |
− | <100 операторов>
| |
− | 3: write(3); | |
− | end.</source>
| |
− | Однако, бывают ситуации, когда удобно использовать оператор <tt>'''goto'''</tt>, например, чтобы ''выйти из нескольких циклов''.
| |
| | | |
− | '''Пример.''' | + | === Арифметические выражения === |
− | :найти a, b, c:
| + | ==== Основные сведения ==== |
− | :a<sup>2</sup> + b<sup>2</sup> = c<sup>2</sup>, 50 <= a,b,c <= 100
| + | Каждое выражение имеет '''тип'''. Выражение называется '''''арифметическим''''', если его тип — ''числовой''. <br /> |
− | <source lang="Pascal">label fin;
| + | Выражение строится посредством '''''операций''''' (унарных или бинарных) и '''''операндов'''''. |
− | var aa, bb, cc: integer;
| |
− | var flag := false;
| |
| | | |
− | for var a:=50 to 100 do
| + | В арифметических выражениях если <tt>a</tt> и <tt>b</tt> — одного типа, то и <tt>a '''op''' b</tt> принадлежит к тому же типу. ''Исключением'' является операция "'''/'''": |
− | for var b:=50 to 100 do
| + | :<tt>'''a / b'''</tt> — вещественное. |
− | for var c:=50 to 100 do
| |
− | if a*a + b*b = c*c then
| |
− | begin
| |
− | flag := true;
| |
− | aa := a;
| |
− | bb := b;
| |
− | cc := c;
| |
− | goto fin;
| |
− | end;
| |
| | | |
− | fin: if flag then
| + | Если <tt>a</tt> и <tt>b</tt> принадлежат к различным типам, то выражение принадлежит к "'''''старшему'''''" типу. <br /> |
− | writeln(aa, bb, cc);</source>
| + | Например: |
− | == Вызов подпрограммы на этапе выполнения ==
| + | byte < integer < int64 |
− | С каждой выполняющейся программой связан блок памяти, выделяющийся ей в начале работы и называемый '''программным стеком'''.
| + | integer < real |
− | <br>Программный стек используется для хранения значений всех переменных, констант, а также промежуточных значений в процессе выполнения. | |
− | <здесь будет рисунок>
| |
− | При в ходе в подпрограмму в процессе выполнения, на программном стеке для нее выделяется специальный участок памяти, называемый '''записью активации''' ('''ЗА''') этой подпрограммы.
| |
| | | |
− | Данный участок выделяется на вершине стека, после чего вершина стека <tt>'''top'''</tt> "поднимается на размер ЗА.
| + | ==== Стандартные функции ==== |
− | <br>После окончания работы подпрограммы ее ЗА снимается со стека и вызывающая программа выполняется дальше.
| + | В арифметические выражения могут входить стандартные функции: |
| + | '''exp'''(x) |
| + | '''ln'''(x) |
| + | '''abs'''(x) // модуль x |
| + | '''sin'''(x) |
| + | '''cos'''(x) |
| + | '''sqr'''(x) // квадрат x |
| + | '''sqrt'''(x) // корень из x |
| + | '''round'''(x) - целое, полученное в результате округления вещественного x |
| + | '''trunc'''(x) - целое, полученное в результате отбрасывания дробной части у вещественного x |
| + | '''min'''(x,y) |
| + | '''max'''(x,y) |
| + | '''power'''(x,y)// x в степени y |
| + | '''random'''(n)// псевдослучайное целое число от 0 до n-1 |
| + | '''random'''(a,b)// псевдослучайное целое число от a до b |
| | | |
− | '''Пример.'''
| + | <xh4> Порядок выполнения операций в арифметических выражениях </xh4> |
− | <здесь будет рисунок>
| + | * Операции с большим приоритетом выполняются первыми |
− | === Устройство ЗА подпрограммы ===
| + | * Функции вычисляются до операций |
− | <здесь будет рисунок>
| + | * Выражение в скобках вычисляется раньше |
− | === Алгоритм входа в подпрограмму ===
| + | * Операции с одинаковым приоритетом выполняются слева направо, если идут подряд. |
− | # Увеличить '''<tt>top</tt>''' стека на размер ЗА п/п, вычесленного на этапе компиляции: '''<tt>top += r<sub>ЗА</sub></tt>'''
| |
− | # Вычислить '''адрес возврата''' ('''АВ''') и '''адрес конца ЗА''' вызывающей п/п
| |
− | # Вычислить значения всех фактических параметров и подставить их на место формальных.
| |
− | # Перейти в коде программы на начало выполнения п/п
| |
− | === Алгоритм выхода из подпрограммы ===
| |
− | # Уменьшить<tt>'''top'''</tt> стека, переместив его на '''адрес конца ЗА''' вызывающей подпрограммы
| |
− | # В коде программы перейти по '''АВ'''
| |
− | <small>'''Лекция 13'''</small>
| |
− | === Пример ===
| |
− | <здесь будет рисунок>
| |
| | | |
− | После запуска программы ОС записывает код этой программы в оперативную память и выделяет дополнительный участок памяти для данных, называемый '''программным стеком'''.
| + | <xh4> Операции div и mod для целых </xh4> |
| + | <tt>x '''div''' y = x / y, округленное до ближайшего целого по направлению к нулю.</tt> Это '''''результат''''' от ''целочисленного деления''. <br /> |
| + | <tt>x '''mod''' y = x - (x div y) * y.</tt> Это '''''остаток''''' от ''целочисленного деления''. |
| | | |
− | При перемещении <tt>top</tt> на программном стеке накапливается мусор, потом в этот мусор опять записывается ЗА.
| + | ''<u>Пример</u> использования'' <br /> |
− | <br />Коды <tt>top</tt> и <tt>current</tt> хранятся в специальных регистрах. | + | Целочисленные операции часто применяются для определения '''четности''' числа: |
| + | x '''mod''' 2 = 0 <-> x — четное |
| + | x '''mod''' 2 <> 0 <-> x — нечетное |
| | | |
− | '''Вывод 1.''' Если подпрограмма — маленькая, то накладные расходы на её вызов могут существенно превысить время работы самой подпрограммы.
| + | === Логические выражения === |
− | <br />'''Вывод 2.''' Если локальные переменные или формальные параметры, передаваемые по значению имеют большой размер, то ЗА соответствующей подпрограммы будет большой. В результате при нескольких вложенных вызовах таких подпрограмм может произойти '''переполнение программного стека''' ('''Stack overflow'''), т.к. программный стек не увеличивается в размерах, а память под него выделяется один раз. | + | <xh4> Основные сведения </xh4> |
− | :'''''Выход.''''' Большие параметры по этой причине рекомендуется передавать не по значению, а ''по ссылке'', при этом на стек копируется не содержимое параметра, а его адрес.
| + | Выражение назывется '''логическим''', если оно имеет тип <tt>'''boolean'''.</tt> <br /> |
| + | ''<u>Пример</u>''. |
| + | x < 0 |
| + | a >= b |
| + | a <> 3 |
| | | |
− | '''Замечание.''' Адреса глобальных переменных, используемых в подпрограммах вычисляются по некоторому алгоритму ''на этапе выполнения'', следовательно, обращение к глобальным переменным происходит дольше, чем к локальным. | + | Это простые логические выражения. Однако, с помщью '''логических операций''' можно составлять сложные. |
| + | (бинарные) (унарные) |
| + | a '''and''' b '''not''' a |
| + | a '''or''' b |
| + | a '''xor''' b |
| | | |
− | ==Модули==
| + | <xh4> Таблицы истинности логических операций </xh4> |
− | === Введение ===
| + | a | b | a '''and''' b | a '''or''' b | a '''xor''' b |
− | '''Модуль''' — это совокупность взаимосвязанных процедур, функций, типов, переменных и констант, | + | |
− | предназначенных для решения ряда однотипных задач, и помещенных в специальным образом оформленный файл.
| + | T | T | T | T | F |
| + | T | F | F | T | T |
| + | F | T | F | T | T |
| + | F | F | F | F | F |
| | | |
− | Модули разбивают большой проект на относительно независимые части, при этом, каждая часть "живет своей жизнью": модуль, написанный для одного программного проекта может быть использован в другом программном проекте.
| + | <xh4> Сокращение вычислений логических выражений </xh4> |
| + | Большинство современных компиляторов, в т.ч. PascalABC.NET производит '''''сокращенное вычисление логических выражений'''''. <br /> |
| + | Это означает, что в выражении |
| + | a '''and''' b |
| + | если a — ложно, то <tt>b</tt> не вычисляется, а в |
| + | a '''or''' b |
| + | если <tt>a</tt> — истинно, <tt>b</tt> не вычисляется. |
| | | |
− | Различают модули в виде '''''исходных текстов''''' и '''''откомпилированные модули'''''.
| + | Это очень полезно при вычислении таких выражений, как, например, |
− | <br>Откомпилированные модули уменьшают суммарное время компиляции и позволяют ''скрывать программный код от модификации''.
| + | (y <> 0) '''and''' (x / y > 0) |
| | | |
− | '''Пример.''' | + | Логически здесь все верно, однако, если бы не использовалось сокращенное вычисление, в случае равенства нулю <tt>y</tt>'а возникала бы ошибка деления на ноль. |
− | :Модуль ''MyLib''
| |
− | <source lang="Pascal">unit MyLib;
| |
| | | |
− | interface // раздел интерфейса
| + | <xh4> Логические переменные </xh4> |
− | const
| + | Можно описывать логические переменные (тип <tt>'''boolean'''</tt>). Им можно присваивать логические выражения. <br /> |
− | MyPi = 3.14;
| + | Эти переменные принимают одно из двух возможных значений: |
− | var
| + | :<tt>'''true'''</tt> (истина) |
− | size: integer := 10;
| + | :<tt>'''false'''</tt> (ложь) |
| | | |
− | function AddSquares(a, b: real): real;
| + | ''<u>Пример</u> использования логических переменных'' <br /> |
− | procedure InvertPrint(a, b: integer);
| + | Дано: прямоугольник со сторонами, параллельными осям координат, задан координатами абсцисс вертикальных сторон (<tt>x1, x2</tt>) и ординатами горизонтальных (y1, y2); точка M( x, y ); <br /> |
| + | Найти: находится ли точка внутри прямоугольника, снаружи, или лежит на границе; |
| | | |
− | implementation // раздел реализации
| + | <source lang="Pascal"> |
− | var i: integer; | + | var inside, outside, bound: boolean; |
− | | |
− | function MySqr(a: real): real;
| |
| begin | | begin |
− | result := a * a; | + | inside := (x > x1) and (x < x2) and (y > y1) and (y < y2); |
− | end; | + | outside := (x < x1) or (x > x2) or (y < y1) or (y > y2); |
| + | bound := not inside and not outside; |
| + | end. |
| + | </source> |
| | | |
− | function AddSquares(a, b: real): real;
| + | === Побитовые операции === |
− | begin
| + | <xh4> Побитовые операции and, or, xor, not </xh4> |
− | result := MySqr(a) + MySqr(b);
| + | '''Замечание.''' Работают только с целыми. |
− | end;
| |
| | | |
− | procedure InvertPrint(a, b: integer);
| + | Соответствующая операция применяется к каждому биту двоичного представления числа. <br /> |
− | begin
| + | ''<u>Пример</u>.'' |
− | write(b, ' ', a);
| + | 5 '''and''' 7 |
− | end;
| + | 5<sub>10</sub> = 101<sub>2</sub> <br /> |
| + | 7<sub>10</sub> = 111<sub>2</sub> |
| | | |
− | end. // конец модуля</source>
| + | 101 |
| + | ( '''and''' ) |
| + | 111 |
| + | ——— |
| + | 101<sub>2</sub> = 5<sub>10</sub> |
| | | |
− | :Программа, подключающая модуль ''MyLib''
| + | <xh4> Операции shl и shr </xh4> |
− | <source lang="Pascal">uses MyLib;
| + | '''''Побитовый сдвиг влево''''' и '''''сдвиг вправо''''' соответственно. |
| | | |
− | var
| + | <xh4> shl </xh4> |
− | n: integer;
| + | x '''shl''' n = x * 2<sup>n</sup> |
| | | |
− | begin
| + | Сдвигает двоичное представление <tt>x</tt> на <tt>n</tt> позиций влево. |
− | writeln(size);
| |
− | writeln(AddSquares(3, 4));
| |
− | InvertPrint(3,4);
| |
− | end.</source>
| |
| | | |
| + | <xh4> shr </xh4> |
| + | x '''shr''' n = x div 2<sup>n</sup> |
| | | |
− | Модуль в языке Object Pascal состоит из двух разделов:
| + | Сдвигает двоичное представление <tt>x</tt> на <tt>n</tt> позиций вправо. |
− | *раздел '''интерфейса'''
| |
− | *раздел '''реализации'''
| |
| | | |
− | В разделе '''интерфейса''' описываются все
| + | <xh4> Примеры </xh4> |
− | <br>''переменные, константы, типы, заголовки подпрограмм'', | + | x = 5<sub>10</sub> = 101<sub>2</sub> |
− | <br>которые можно будет использовать в других модулях, подключающих данный. | |
− | | |
− | В разделе '''реализации''' содержится
| |
− | <br>''реализация подпрограмм'', | |
− | <br>заголовки которых приведены в разделе интерфейса, а также | |
− | <br>описание ''вспомогательных констант, переменных, типов и подпрограмм'',
| |
− | <br>которые нужны для реализации подпрограмм из раздела интерфейса и не видны из других модулей.
| |
− | | |
− | Данный принцип получил название '''принципа сокрытия информации'''.
| |
− | <br>Его ''преимущества'':
| |
− | *уменьшение количества информации, необходимой для понимания работы модуля;
| |
− | *сокрытие реализации от клиентов;
| |
− | *возможность легкого изменения реализации в следующих версиях без изменения интерфейса, т.е. без затрагивания интерфейса клиентов;
| |
− | *возможность скрыть программный код от клиентов, предоставив им лишь откомпилированную версию модуля;
| |
− | | |
− | <small>'''Лекция 14'''</small> | |
− | === Синтаксис модуля ===
| |
− | '''unit''' <имя модуля>;
| |
| | | |
− | '''interface''' | + | x shl 2 = <—(<sup>2</sup>)101 |
− | ['''uses''' <список модулей>;]
| + | 10100<sub>2</sub> = 20<sub>10</sub> |
− | <раздел описаний модуля: для п/п — только заголовки>
| |
| | | |
− | '''implementation''' | + | x shr 2 = 101—>(<sup>2</sup>) |
− | ['''uses''' <список модулей>;]
| + | 001<sub>2</sub> = 1<sub>10</sub> |
− | <раздел описаний, где даны реализации п/п из раздела interface>
| |
− |
| |
− |
| |
− | ['''initialization'''
| |
− | <операторы>] ''' begin'''
| |
− | | <операторы>
| |
− | ['''finalization''' '''end'''.
| |
− | <операторы>]
| |
| | | |
− | === Семантические ограничения === | + | === Таблица приоритетов операций языка Object Pascal === |
− | *Модули, перечисленные в секции <tt>'''uses'''</tt> в разделах интерфейса и реализации не должны пересекаться;
| + | # унарные <tt>'''+ - not'''</tt> |
− | *''циклическая зависимость'' между модулями в разделе интерфейса '''запрещена''':
| + | # имеющие смысл ''умножения'' <tt>'''* / div mod and shl shr'''</tt> |
− | '''unit''' A; '''unit''' B;
| + | # имеющие смысл ''сложения'' <tt>'''+ - or xor'''</tt> |
− | '''interface''' '''interface'''
| + | # операции ''отношения'' <tt>'''<> <= >= < > in'''</tt> |
− | '''uses''' B; '''uses''' A;
| |
− | :тем не менее, если A ссылается на B в разделе ''интерфейса'', а B ссылается на A в разделе ''реализации'', то это '''допустимо''':
| |
− | '''unit''' A; '''unit''' B;
| |
− | '''interface''' '''interface'''
| |
− | '''uses''' B; '''implementation'''
| |
− | '''uses''' A;
| |
− | :потому что модули '''''компилируются в два этапа''''':
| |
− | :*интерфейс
| |
− | :*реализация
| |
| | | |
− | === Разделы инициализации и финализации модуля === | + | === Условный оператор === |
− | '''unit''' A; <u>основная программа</u>
| + | <xh4> Синтаксис </xh4> |
− | ...
| + | '''if''' <условие> '''then''' <оператор<sub>1</sub>> |
− | '''initialization''' '''uses''' A; | + | ['''else''' <оператор<sub>2</sub>>] |
− | <операторы<sub>1</sub>>
| |
− | '''finalization ''' '''begin'''
| |
− | <операторы<sub>2</sub>> <операторы<sub>3</sub>>
| |
− | '''end'''. '''end'''.
| |
| | | |
− | Операторы в разделе '''<tt>initialization</tt>''' модуля выполняются ''раньше'' тела основной программы,
| + | <xh4> Семантика </xh4> |
− | <br /> а операторы в разделе <tt>'''finalization'''</tt> выполняются''после'' основной программы.
| + | [[Изображение: If.jpg]] |
| | | |
− | Т.е. последовательность выполнения будет такой:
| + | ==== Примеры использования для решения задач ==== |
− | <операторы<sub>1</sub>>
| + | ''<u>Пример 1</u>.'' Нахождение минимума |
− | <операторы<sub>3</sub>>
| + | <br>Дано: <tt>x, y</tt> |
− | <операторы<sub>2</sub>>
| + | <br>Найти: <tt>min</tt> |
− | '''Примечание.''' В разделе ''инициализации'' обычно инициализируются глобальные переменные из раздела интерфейса.
| |
| | | |
− | <u>Пример</u>.
| + | <source lang="Pascal"> |
− | <source lang="Pascal">unit A; | + | if x > y then |
− | | + | min := y |
− | interface
| + | else |
− | var
| + | min := x; |
− | RandomNumber: integer; | |
− | | |
− | implementation
| |
− | ...
| |
− | initialization
| |
− | RandomNumber := Random(100); | |
− | end.
| |
| </source> | | </source> |
| | | |
− | === Схема компиляции программы с модулями ===
| + | ''<u>Пример 2</u>.'' Упорядочение <tt>a, b</tt> по возрастанию. |
− | <Здесь будет рисунок>
| + | <br>Ясно, что если a > b, — нужно [[Основы программирования — Осенний семестр; Михалкович С.С.; 2008; II#Примеры использования := | поменять их местами]]. <br /> |
− | | + | Но тут одним оператором не обойтись. |
− | 1. Компилируется файл основной программы.
| + | Для этого можно использовать '''''составной оператор''''' — один или больше операторов, заключенных в операторные скобки <tt>'''begin''' - '''end''';</tt>: |
− | <br />2. Если в нем встречена секция <tt>'''uses'''</tt>, то компилируются модули из этой секции ''слева направо''. | + | <source lang="Pascal">if a > b then |
− | <br />3. Каждый модуль компилируется так же, как и основная программа по пунктам 1-2: | |
− | :Если в модуле подключаются другие модули, то компилируются они,
| |
− | :и так происходит до тех пор, пока компилятор не дойдет до модулей, не содержащих подключения других модулей.
| |
− | 4. По окончании компиляции модуля его откомпилированный вариант (.'''pcu''' в PascalABC.NET) записывается на диск.
| |
− | <br />5. После записи откомпилированного модуля на диск компилятор возвращается к основному модулю (вызывающему) или программе, и докомпилирует его до конца. Основная программа после компиляции хранится в оперативной памяти. | |
− | <br />6. ''Первый этап компиляции закончен''. Начинается заключительный этап компиляции — '''линковка''' ('''компоновка'''). Специальная программа — '''линковщик''' — собирает из откомпилированных модулей единый исполняемый файл (.'''exe''' в Windows).
| |
− | | |
− | '''Замечание 1.''' Ошибки могут происходить как на этапе компиляции, так и на этапе, собственно, линковки.
| |
− | <br />'''Замечание 2.''' Наличие откомпилированного файла модуля существенно ускоряет процесс компиляции (сводя его к процессу линковки).
| |
− | <br />'''Замечание 3.''' Если есть откомпилированные файлы модулей, то исходные тексты модулей можно удалить. При этом компилятор проанализирует, что данный модуль откомпилирован, и продолжит компиляцию дальше.
| |
− | <br />'''Замечание 4.''' Если файл модуля <tt>.pas</tt> создан после откомпилированного файла модуля (<tt>.pcu</tt>), то произойдет его перекомпиляция.
| |
− | <br />'''Замечание 5.''' При работе в интегрированной среде компилятор в первую очередь берет тексты модулей из открытых файлов и только затем смотрит файлы на диске.
| |
− | | |
− | '''Примечание 1.''' Файлы модулей могут храниться в другом каталоге относительно файла исходной программы. Тогда надо вместе с именем модуля указывать, в каком каталоге он хранится (тогда имя модуля и имя файла модуля могут не совпадать):
| |
− | — Main ——————————— MyPr.pas
| |
− | |
| |
− | |— Modules | — a.pas
| |
− | | — b. pas
| |
− | | |
− | <u>MyPr.pas</u>
| |
− | '''program''' MyPr;
| |
− | '''uses'''
| |
− | A '''in''' 'Modules\a.pas';
| |
− | B '''in''' 'Modules\b.pas';
| |
− | '''Примечание 2.''' Откомпилированные файлы модулей по умолчанию создаются:
| |
− | *в каталоге основной программы
| |
− | *если установлен специальный путь в среде программирования для выходных файлов, то по этому пути
| |
− | Кроме этого, стандартные откомпилированные модули хранятся в специальной папке, например, в PascalABC.NET — в папке '''\LIB'''. Если модуль претендует на звание стандартного, его можно туда поместить.
| |
− | | |
− | === Упрощенная структура модуля === | |
− | В PascalABC.NET можно использовать упрощенную структуру модуля:
| |
− | '''unit''' <имя модуля>;
| |
− | <раздел описаний>
| |
− | ['''begin'''
| |
− | <операторы>]
| |
− | '''end'''.
| |
− | | |
− | В <разделе описаний> описываются типы, константы, переменные и подпрограммы.
| |
− | <br />Раздел <tt>'''begin''' + <операторы></tt> соответствует разделу ''инициализации''. | |
− | | |
− | === Алгоритм поиска имен в модулях ===
| |
− | В разных модулях могут находиться объекты с одинаковыми именами, т.к. модуль является ''пространством имен''.
| |
− | <br />Как же осуществляется поиск имен в модулях?
| |
− | | |
− | '''Правило 1'''. Имя вначале ищется в текущем модуле. Если не найдено, то в подключенных модулях секции <tt>'''uses'''</tt> ''справа налево''. | |
− | <br />'''Правило 2'''. Если нужно явно указать переменную из какого-то модуля, то перед ней ставится <tt>'''<имя модуля>.'''</tt>
| |
− | | |
− | ''<u>Пример</u>''.
| |
− | <source lang="Pascal">unit A;
| |
− | uses B;
| |
− | var n, m: integer;
| |
− | ...
| |
− | end.</source>
| |
− | | |
− | <source lang="Pascal">unit B; | |
− | var n, k, m: integer;
| |
− | ...
| |
− | end.</source>
| |
− | | |
− | <source lang="Pascal">program MyPr;
| |
− | uses A, B;
| |
− | | |
− | var n: integer;
| |
| begin | | begin |
− | n := 5; // из основной программы | + | var v := b; |
− | k := 6; // из B | + | b := a; |
− | m := 7; // из B | + | a := v; |
− | A.n := 8; // из A
| + | end; |
− | B.n := 9; // из B
| |
− | A.m := 10; // из A
| |
− | end.</source> | |
− | | |
− | === Стандартный системный модуль PABCSystem ===
| |
− | <xh4></xh4>
| |
− | <source lang="Pascal"></source>
| |
− | | |
− | <small>'''Лекция 15'''</small>
| |
− | | |
− | Документирующие комментарии ///
| |
− | | |
− | Библиотеки. Отличие библиотек от модулей. Подключение библиотек в PascalABC.NET.
| |
− | | |
− | Перечислимый и диапазонный типы. Стандартные функции Ord Pred Succ. Стандартные процедуры Inc Dec.
| |
− | | |
− | Использование перечислимого типа в for и case.
| |
− | | |
− | ==Массивы==
| |
− | | |
− | Понятие структурированного типа данных.
| |
− | | |
− | Определение массива.
| |
− | | |
− | Описание массивов. Тип массива. Тип индексов массива. Понятие порядкового типа.
| |
− | | |
− | <small>'''Лекция 16'''</small>
| |
− | | |
− | Обращение к элементам массивов.
| |
− | | |
− | Выход за границы диапазона. Контроль выхода в Delphi и PascalABC.NET.
| |
− | | |
− | Динамические массивы. Хранение в памяти.
| |
− | | |
− | Инициализация массивов.
| |
− | | |
− | Присваивание массивов. Сравнение массивов.
| |
− | | |
− | Присваивание a := nil для динамических массивов.
| |
− | | |
− | Цикл foreach.
| |
− | | |
− | Именная и структурная эквивалентность типов.
| |
− | | |
− | <small>'''Лекция 17'''</small>
| |
− | <H3>Массивы как параметры подпрограмм</H3>
| |
− | | |
− | При передаче статического массива должна соблюдаться именная эквивалентность
| |
− | <source lang="delphi">
| |
− | procedure xxx(a: array [1..100] of integer); // неверно
| |
− |
| |
− | type IArr = array [1..100] of integer;
| |
− | procedure yyy(a: IArr);
| |
| </source> | | </source> |
− | Передавать массив по значению крайне неэффективно (т.к.происходит копирование всех элементов на стек). Его следует передавать по ссылке: для этого есть специальное ключевое слово <tt>const</tt>, которое также запрещает изменять данные внутри п/п.
| |
− | <source lang="delphi">
| |
− |
| |
− | procedure zzz(const a: IArr);
| |
− | procedure sss(var a: IArr);
| |
| | | |
| + | ''<u>Пример 3</u>.'' Вычисление функции по взаимоисключающим веткам <br /> |
| + | <math>y = \begin{cases} x, & x < 2 \\ x^2, & 2 < x < 3 \\ 1-x, & x \ge\; 3 \end{cases}</math> |
| + | <source lang="Pascal"> |
| + | if x < 2 then |
| + | y := x |
| + | else if x < 3 then |
| + | y := x * x |
| + | else y := 1 - x; |
| </source> | | </source> |
− | Динамический массив
| |
− | можно передавать в виде <tt>array of integer</tt>, т.к. для него определена структурная эквивалентность типов.
| |
− | <source lang="delphi">
| |
− | procedure ttt(a: array of integer);
| |
− | </source>
| |
− | Передавать массив по ссылке необходимо только если внутри п/п нужно изменить длину массива.
| |
− | Иначе можно передавать по значению, т.к. при вызове п/п на программный стек кладется только адрес участка памяти, хранящего значения элементов массива, а значит все изменения формального массива-параметра внутри п/п меняют соответствующий ему фактический параметр.
| |
− | Массивы как возвращаемые значения функции
| |
| | | |
− | Часто требуется использовать массив как возвращаемое значение функции, для этого удобно использовать динамический массив, который хранит свою длину.
| + | '''Замечание.''' Если по ветви '''<tt>else</tt>''' располагается другой оператор '''<tt>if</tt>''', то говорят, что возникает '''''цепочка вложенных операторов <tt>if</tt>'''''. |
| | | |
− | <h3>Переменное число параметров подпрограмм</h3> | + | ====Координаты средней точки==== |
| + | ''<u>Пример 4</u>.'' Даны координаты трех точек на прямой <tt>a, b, c</tt> (<tt>a, b, c</tt> попарно не равны). Найти m - координаты средней точки <br /> |
| | | |
− | П/п можно создавать с переменным количеством параметров.
| + | '''Решение 1.''' |
− | Для этого исп. ключевое слово params.
| |
− | При этом такие параметры, перечисленные через запятую, на этапе выполнения упаковываются в динамический массив, и п/п внутри работает именно с ним.
| |
− | Пар-р params может быть только один и должен находиться последним в списке пар-ов.
| |
| | | |
− | <small>'''Лекция 18'''</small>
| + | Достаточно рассмотреть случай, когда координаты точек равны 1,2 и 3 |
− | ==Шаблоны подпрограмм==
| |
| | | |
− | Часто требуется выполнять одно и то же действие для разных типов данных.
| + | Рассмотрим все возможные ситуации распределения этих значений между a,b,c: |
− | В этом случае неудобно использовать перегрузку, т.к. п/п отличаются только заголовками.
| |
| | | |
− | Выход - шаблоны.
| + | a b c |
| + | 1 2 3 a<b |
| + | 1 3 2 a<b |
| + | 2 1 3 |
| + | 2 3 1 a<b |
| + | 3 1 2 |
| + | 3 2 1 |
| | | |
− | Пример:
| + | При a<b возможны три варианта, в двух из которых a<c. |
| + | При a>b возможны три варианта, в двух из которых a>c. |
| | | |
− | procedure Print<T> (array of T);
| + | Решение представляется вложенными операторами if с уровнем вложенности 3. |
| | | |
− | В момент вызова п/п происходит:
| + | <source lang="Pascal"> |
− | | + | if a < b then |
− | * выведение типов шаблона по фактическим параметрам
| + | if a < c then |
− | * инстанцирование - подстановка конкретных типов шаблонов и генерация кода подпрограммы с этими конкретными типами.
| + | if b < c then |
− | | + | m := b |
− | Если для такого типа код уже инстанцировался, то повторно действие не выполняется.
| + | else |
− | | + | m := c |
− | ==Задачи на одномерные массивы== | + | else |
− | | + | m := a |
− | * инвертирование массива (шаблон)
| + | else |
− | * поиск элемента в массиве - найти и вернуть индекс (шаблон)
| + | if a > c then |
− | | + | if b < c then |
− | можно с for и break,
| + | m := c |
− | | + | else |
− | можно и другим способом - с while
| + | m := b |
− | | + | else m := a; |
− | * поиск с барьером (шаблон). Его преимущества
| |
− | * минимальный элемент массива и его индекс
| |
− | * слияние двух упорядоченных массивов в один упорядоченный (барьер)
| |
− | * сдвиг элементов массива влево (шаблон)
| |
− | | |
− | знакомство со значением default(T), Т - тип
| |
− | | |
− | <small>'''Лекция 19'''</small> | |
− | | |
− | <h3>Задачи на одномерные массивы</h3>
| |
− | | |
− | pas-файл с задачами
| |
− | | |
− | doc-файл
| |
− | | |
− | используется тип IArr = array [1..size] of integer
| |
− | | |
− | * сдвиг элементов массива вправо (ShiftRight)
| |
− | | |
− | [ два варианта определения граничных значений цикла (от n-1 до 1; от n до 2) ]
| |
− | | |
− | * циклический сдвиг элементов массива вправо (CycleShiftRight)
| |
− | * удаление элемента по заданному индексу (Delete)
| |
− | * вставка элемента по заданному индексу (Insert)
| |
− | | |
− | <H3>Задачи с процедурными переменными в качестве параметров</H3> | |
− | | |
− | Определяется тип IUnPred = function (x: integer): boolean
| |
− | | |
− | * поиск элемента по заданному условию (Find)
| |
− | * количество элементов, удовлетворяющих условию (Count)
| |
− | * условный минимум (MinElem)
| |
− | * удаление всех элементов, удовлетворяющих условию (DeleteAll)
| |
− | | |
− | '''Примечание.''' Для проверки выхода индекса за границы диапазона (внутри цикла) следует проверять граничные значения счетчика, подставляя их во все индексы
| |
− | | |
− | <small>'''Лекция 20'''</small>
| |
− | | |
− | ==Сортировки==
| |
− | | |
− | * '''Выбором''' (нахождение минимального элемента в неупорядоченной части массива и перемещение его на первую после уже отсортированного участка позицию)
| |
− | | |
− | * '''Пузырьковая'''
| |
− | | |
− | Это самый компактный алгоритм сортировки. Его можно оптимизировать, проверяя случай "холостого" прохода по элементам массива. Т.е. если за проход ни один элемент не изменил позицию, значит массив уже отсортирован и проверять дальше нет смысла.
| |
− | | |
− | * '''Вставками''' (вставка элемента из еще не упорядоченного участка массива в "нужную" позицию отсортированного, для чего сдвиг части элементов вправо и вставка нового в освободившуюся позицию)
| |
− | | |
− | '''Замечание.''' Рассмотренные виды сортировки не являются самыми эффективными.
| |
− | Асимптотическая сложность алгоритмов
| |
− | | |
− | '''Определение.''' Число шагов алгоритма асимптотически равно Θ(f(n)) если, существуют такие N, с1, с2>0 (c1<c2), что для любых n ≥ N выполняется:
| |
− | | |
− | c1*f(n) ≤ число шагов ≤ c2*f(n) | |
− | | |
− | '''Замечание 1.''' Для рассмотренных алгоритмов сортировки асимптотическая сложность = Θ(n<sup>2</sup>).
| |
− | | |
− | '''Замечание 2.''' Существуют алгоритмы сортировки с асимптотической сложностью = Θ(n*logn) (они будут рассмотрены в следующем семестре).
| |
− | | |
− | <small>'''Лекция 21'''</small>
| |
− | ==Алгоритм бинарного поиска в отсортированном массиве==
| |
− | | |
− | Его асимптотическая сложность - Θ(logn).
| |
− | Двумерные массивы
| |
− | | |
− | Двумерный массив как матрица (соответствие индексов строкам и столбцам)
| |
− | | |
− | Элементы строки. Элементы столбца (рисунки)
| |
− | | |
− | Понятие замороженного индекса
| |
− | | |
− | <H3>Задачи на двумерные массивы</H3> | |
− | | |
− | * Вывод матрицы
| |
− | ** внешний цикл по строкам
| |
− | ** если внешним сделать цикл по столбцам будет выведена транспонированная матрица
| |
− | | |
− | * Сумма элементов в j-том столбце
| |
− | | |
− | * Сумма элементов в каждом столбце
| |
− | ** использование функции для нахождения суммы в j-ом столбце
| |
− | * Минимальный элемент в каждой строке
| |
− | ** в отличие от предыдущей задачи, для реализации не создается дополнительная п/п.
| |
− | | |
− | '''Замечание.''' Решать такие задачи можно и реализуя алгоритм полностью, и создавая для этого дополнительные п/п. Если задача несложная, то вполне можно пользоваться первым способом, однако если решение не очевидно, лучше выносить часть алгоритма в п/п, иначе код будет очень трудно читать.
| |
− | | |
− | * Квадратные матрицы и элементы выше-ниже главной (побочной) диагонали.
| |
− | | |
− | <small>'''Лекция 22'''</small>
| |
− | | |
− | * Поиск элемента в матрице
| |
− | | |
− | если для поиска используется функция, то при нахождении элемента выходить из циклов с помощью exit,
| |
− | | |
− | иначе удобно использовать goto КОНЕЦ_ВНЕШНЕГО_ЦИКЛА
| |
− | | |
− | ==Записи==
| |
− | | |
− | <source lang="delphi">
| |
− | type <имя записи>= record
| |
− | <поля записи> | |
− | end;
| |
| </source> | | </source> |
| | | |
− | Поля записей
| + | По количеству операций (3 сравнения, одно присваивание) это - самое лучшее решение |
| | | |
− | Инициализация записей при описании (на примере Student)
| + | То же решение, записанное с помощью функции min: |
| | | |
− | Доступ к полям записей. Точечная нотация.
| + | <source lang="Pascal"> |
− | | + | if a < b then |
− | '''Замечание.''' Для записей имеет место именная эквивалентность типов.
| + | if a < c then |
− | | + | m := min(b,c) |
− | Передача записей в п/п (Print(s)). Запись как возвращаемое значение функции (s := ReadStudent;).
| + | else m := a |
− | | + | else |
− | Передавать записи по значению неэффективно, т.к. копируются все поля. Поэтому следуют передавать их по ссылке (с ключевым словом const - если значения полей не изменяются, и var - если внутри п/п их требуется изменить.
| + | if a > c then |
− | | + | m := min(b,c) |
− | Оператор with.
| + | else m := a; |
− | | |
− | <source lang="delphi">
| |
− | with <переменная типа запись> do
| |
− | begin
| |
− | <поле>:=<значение>
| |
− | end;
| |
| </source> | | </source> |
| | | |
− | Оператор удобен, когда нужно выполнить достаточное количество операций с одной и той же переменной (имя этой переменной и точка добавляются неявно перед всеми полями)
| + | '''Решение 2.''' |
| | | |
− | <H3>Методы в записях</H3>
| + | <source lang="Pascal"> |
− | | + | var m1 := min(a,b); |
− | На примере Student.Print.
| + | var m2 := max(a,b); |
− | | |
− | Запись как пространство имен
| |
− | | |
− | Запись выступает для своих полей и методов в качестве модуля (поскольку является набором данных и связанных с ними процедур и функций, оформленных как единое целое).
| |
− | | |
− | Методы работают в некотором окружении, в роли которого выступают поля записи и другие методы.
| |
− | | |
− | Создается окружение при описании переменной типа запись.
| |
− | | |
− | * Сравнение s.Print и Print(s)
| |
− | | |
− | '''s.Print''' - объектно-ориентированный подход, доминирует объект, который сам выполняет над собой действие.
| |
− | | |
− | '''Print(s)''' - процедурно-ориентированный подход, главным является действие, выполняющееся над переменной.
| |
− | | |
− | * Метод Init - инициализатор полей записи.
| |
− | | |
− | <small>'''Лекция 23'''</small>
| |
− | ==Создание простейших графических объектов==
| |
− | | |
− | <source lang="delphi"> | |
− | RPoint = record x, y: integer end;
| |
− | RRectangle = record p1,p2: RPoint end;
| |
| </source> | | </source> |
| | | |
− | Для каждого типа определяются методы Print, Draw, MoveOn
| + | a b c |
| + | 1 2 3 c>m2 |
| + | 1 3 2 |
| + | 2 1 3 c>m2 |
| + | 2 3 1 c<m1 |
| + | 3 1 2 |
| + | 3 2 1 c<m1 |
| | | |
− | <H3>Записи как средство упаковки параметров</H3>
| + | <source lang="Pascal"> |
− | | + | var m1 := min(a,b); |
− | '''Пример.'''
| + | var m2 := max(a,b); |
− | Используем определенные типы записей для создания перегруженных версий процедур Rectangle и Line:
| + | if c<m1 then |
− | | + | m := m1 |
− | <source lang="delphi"> | + | else if c>m2 then |
− | Rectangle(x1,y1, x2,y2: integer)
| + | m := m2 |
− | Rectangle(p1,p2: RPoint)
| + | else m := c; |
− | Rectangle(r: RRectangle)
| |
− | | |
− | Line(x1,y1, x2,y2: integer)
| |
− | Line(p1,p2: RPoint)
| |
− | Line(r: RRectangle)
| |
| </source> | | </source> |
| | | |
− | <H3>Переменная типа запись как объект</H3>
| + | Данное решение менее эффективно по числу сравнений и присваиваний (посчитайте самостоятельно), но по понятности может восприниматься лучше предыдущего. |
| | | |
− | В модуле можно выносить реализацию методов записи в раздел реализации. При этом в разделе интерфейса внутри определения типа запись описываются только заголовки методов, а в разделе реализации перед именем метода необходимо писать <Имя записи>.
| + | ====Самостоятельные задания==== |
| + | * Даны координаты вершин треугольника и точка M, не лежащая на границе треугольника. Принадлежит ли M треугольнику. |
| + | * Является ли 4-угольник ABCD корректно заданным. |
| | | |
− | Поля записи как состояние объекта.
| + | === Оператор case выбора варианта === |
| + | <xh4> Синтакстис </xh4> |
| + | '''case''' <переключатель> '''of''' |
| + | {<список выбора>: <оператор>;} |
| + | ['''else''' <оператор>[;]] |
| + | '''end''' |
| | | |
− | Методы записи как действия, воздействующие на состояние объекта.
| + | <xh4> Семантика </xh4> |
| + | Вначале вычисляется выражение-<tt><переключатель></tt>, после чего его значение ищется в одном из <tt><списков выбора></tt>. <br /> |
| + | Если значение попадает в какой-то <tt><список выбора></tt>, то выполняется соответствующий ему оператор, иначе, если есть ветвь '''<tt>else</tt>''', то выполняется оператор по ветке <tt>else</tt>. |
| | | |
− | Методы делятся на две категории
| + | <xh4> Ограничения </xh4> |
− | * меняющие состояние объекта (MoveOn для RPoint) | + | * выражение-переключатель должно иметь так называемый '''''порядковый''''' тип: |
− | * осуществляющие доступ к состоянию объекта на чтение (Draw для RPoint) | + | :''целый'' |
| + | :''символьный'' |
| + | :''перечислимый'' |
| + | НО НЕ строковый или вещественный. |
| + | * значения в <tt><списках выбора></tt> не должны пересекаться. |
| | | |
− | Сортировка массива записей.
| + | <xh4> Примеры использования оператора выбора </xh4> |
− | | + | ''<u>Пример 1</u>.'' День недели |
− | <small>'''Лекция 24'''</small> | + | <source lang="Pascal"> |
− | ==Индексная сортировка==
| + | case DayOfWeek of |
− | | + | 1..5: writeln('Будний'); |
− | Часто физически переставлять элементы массива или вообще невозможно, или долго (в силу достаточно большого размера, например).
| + | 6, 7: writeln('Выходный'); |
− | В этом случае удобно использовать ''перестановку индексов'' вместо перестановки самих элементов.
| + | else writeln('Ошибка'); |
− | | |
− | Идея заключается в том, чтобы описать '''индексный массив''' index, который взаимооднозначно сопоставляет ''реальным'' индексам элемента ''виртуальные'' таким образом, чтобы относительно ''виртуальных'' массив оказался упорядоченным.
| |
− | | |
− | ----
| |
− | | |
− | '''Пример.'''
| |
− | | |
− | Дан массив целых (с реальными индексами):
| |
− | '''5'''[1] '''15'''[2] '''3'''[3] '''1'''[4] '''9'''[5] '''8'''[6] '''20'''[7] '''12'''[8]
| |
− | | |
− | А так выглядит отсортированный массив по виртуальным индексам:
| |
− | '''1'''[1] '''3'''[2] '''5'''[3] '''8'''[4] '''9'''[5] '''12'''[6] '''15'''[7] '''20'''[8]
| |
− | | |
− | Т.е.
| |
− | index[1] = 4
| |
− | index[2] = 3
| |
− | index[3] = 1
| |
− | index[4] = 6
| |
− | index[5] = 5
| |
− | index[6] = 8
| |
− | index[7] = 2
| |
− | index[8] = 7
| |
− | | |
− | ----
| |
− | Соответственно, алгоритм сортировки меняется таким образом, что вместо перемены местами самих элементов упорядочиваемого массива меняется '''содержимое элементов index'''.
| |
− | | |
− | Вначале удобно положить элементы index соответствующими '''тождественной''' подстановке (т.е. a[i] = i).
| |
− | <source lang="delphi"> | |
− | type CmpFunc = function (const s1,s2: Student): boolean;
| |
− | | |
− | procedure BubbleIndexStudentSort(const a: StArr; var Index: IArr; n: integer; Cmp: CmpFunc);
| |
− | begin
| |
− | for var i:=1 to n do | |
− | Index[i] := i;
| |
− | for var i:=n downto 2 do | |
− | for var j:=1 to i-1 do
| |
− | if Cmp(a[Index[j+1]],a[Index[j]]) then
| |
− | Swap(Index[j+1],Index[j]);
| |
− | end;
| |
− | | |
− | procedure WriteArrByIndex(const a: StArr; const Index: IArr; n: integer);
| |
− | begin
| |
− | for var i:=1 to n do | |
− | writeln(a[Index[i]].name,' ',a[Index[i]].course,' ',a[Index[i]].group);
| |
| end; | | end; |
| </source> | | </source> |
| | | |
− | ==Множества==
| + | ''<u>Пример 2</u>.'' Цифра или буква |
− | | + | <source lang="Pascal"> |
− | Описание множеств. Множества-константы. Пустое множество.
| + | var c: char; |
− | | + | read(c); |
− | Операции над множествами:
| + | case c of |
− | s1+s2 - объединение
| + | '0'..'9': writeln('Цифра'); |
− | s1*s2 - пересечение
| + | 'A'..'Z', 'a'..'z', 'а'..'я', 'А'..'Я', 'ё', 'Ё': writeln('Буква'); |
− | s1-s2 - разность
| |
− | s += s1
| |
− | s -= s1
| |
− | s *= s1
| |
− | s1<s2 - строгое вложение
| |
− | s1<=s2 - нестрогое вложене
| |
− | s1>s2 - строгое вложение
| |
− | s1<=s2 - нестрогое вложене
| |
− | i in s - принадлежность
| |
− |
| |
− | Include(s,i);
| |
− | Exclude(s,i);
| |
− | Вывод множеств.
| |
− | | |
− | Цикл foreach по множеству.
| |
− | | |
− | Пример использования множеств: решето Эратосфена. | |
− | Алгоритм.
| |
− | | |
− | <small>'''Лекция 25'''</small> | |
− | <H3>Алгоритм Эратосфена</H3>
| |
− | | |
− | Код алгоритма Эратосфена
| |
− | <source lang="delphi"> | |
− | const n = 10000;
| |
− | var primes: set of byte;
| |
− | begin
| |
− | primes := [2..n];
| |
− | for var i:=2 to round(sqrt(n)) do
| |
− | begin
| |
− | if not (i in primes) then
| |
− | continue;
| |
− | var x := 2*i;
| |
− | while x<=n do
| |
− | begin
| |
− | Exclude(primes,x);
| |
− | x += i;
| |
− | end;
| |
− | end;
| |
− | for var i:=0 to n do
| |
− | if i in primes then
| |
− | write(i,' ');
| |
− | end.
| |
− | </source>
| |
− | | |
− | <H3>Статические методы записей</H3>
| |
− | <source lang="delphi">
| |
− | type RRectangle = record
| |
− | class function EqualSquare(const r1,r2: RRectangle): boolean;
| |
− | class function CreateRandom: RRectangle;
| |
| end; | | end; |
| </source> | | </source> |
− |
| |
− | <H3>Символы</H3>
| |
− |
| |
− | Коды символов. Однобайтовые и двухбайтовые кодировки.
| |
− | ASCII - стандарт на символы с кодами 0..127.
| |
− | Unicode.
| |
− | Однобайтовые кодировки: Windows, DOS (CP866), Koi8
| |
− |
| |
− | Литеральные константы-символы:
| |
− | #13 - символ возврата "каретки"
| |
− | #10 - символ перехода на следующую строку
| |
− | #9 - символ табуляции
| |
− |
| |
− | <H4>Стандартные подпрограммы работы с символами</H4>
| |
− | OrdUnicode(c)
| |
− | ChrUnicode(i)
| |
− | Succ(c)
| |
− | Pred(c)
| |
− | Inc(c,5)
| |
− | Dec(c,5)
| |
− | Ord(c)
| |
− | Chr(i)
| |
− | UpperCase(c)
| |
− | LowerCase(c)
| |
− |
| |
− | Для символов с кодами 0..127
| |
− | OrdUnicode(c)=Ord(c)
| |
− | ChrUnicode(i)=Chr(i)
| |
− |
| |
− | char - тип, содержащий ряд статических методов:
| |
− |
| |
− | char.IsDigit(c)
| |
− | char.IsLetter(c)
| |
− | char.IsLetterOrDigit(c)
| |
− | char.IsLower(c)
| |
− | char.IsUpper(c)
| |
− | char.ToLower(c)
| |
− | char.ToUpper(c)
| |
− |
| |
− | <small>'''Лекция 26'''</small>
| |
− | ==Строки==
| |
− |
| |
− | Тип string
| |
− |
| |
− | Отводится память (до 2 Гб), равная длине строки, плюс некоторая дополнительная информация
| |
− |
| |
− | Строки - массивы символов, индексируемые с 1: s[i] - i - тый символ строки.
| |
− | var s := 'IT'; // s[1]='I', s[2]='T'
| |
− |
| |
− | Операции со строками:
| |
− |
| |
− | s1+s2
| |
− | s1 += s2
| |
− | s1<s2
| |
− | ...
| |
− |
| |
− | <H3>Основные подпрограммы работы со строками</H3>
| |
− |
| |
− | '''Length(s)''' - функция, возвращающая длину строки s
| |
− |
| |
− | '''SetLength(s,n)''' - процедура, устанавливающая длину строки s равной n
| |
− |
| |
− | '''Copy(s,from,len)''' - функция, возвращающая подстроку s с позиции from длины len
| |
− |
| |
− | '''Insert(subs,s,form)''' - процедура, вставляющая подстроку subs в строку s с позиции from
| |
− |
| |
− | '''Delete(s,from,len)''' - процедура, удаляющая из строки s len символов с позиции from
| |
− |
| |
− | '''Pos(subs,s)''' - функция, возвращающая позицию первого вхождения подстроки subs в строку s или 0, если подстрока не найдена
| |
− |
| |
− | '''PosEx(subs,s,from=1)''' - функция, возвращающая позицию первого вхождения подстроки subs в строку s, начиная с позиции from, или 0, если подстрока не найдена
| |
− |
| |
− | '''TrimLeft(s)''' - функция, возвращающая строку s, в которой удалены все начальные пробелы
| |
− |
| |
− | '''TrimRight(s)''' - функция, возвращающая строку s, в которой удалены все заключительные пробелы
| |
− |
| |
− | '''Trim(s)''' - функция, возвращающая строку s, в которой удалены все удаляет все начальные и заключительные пробелы
| |
− |
| |
− | <h4>Преобразование строка ↔ число</h4>
| |
− |
| |
− | '''Str(x,s)''' - процедура, преобразующая целое или вещественное выражение x к строковому представлению и записывающая результат в строку s
| |
− |
| |
− | '''Val(s,x,errcode)''' - процедура, преобразующая строку s к целому или вещественному значению и записывающая результат в целую или вещественную переменную x. Переменная errcode - целая; если преобразование невозможно, то в errcode содержится номер первого символа, вызвавшего ошибку
| |
− |
| |
− | '''IntToStr(i)''' - функция, преобразующая целое x в строку
| |
− |
| |
− | '''StrToInt(s)''' - функция, преобразующая строку s к целому; может генерировать исключение
| |
− |
| |
− | '''FloatToStr(i)''' - функция, преобразующая вещественное x в строку
| |
− |
| |
− | '''StrToFloat(s)''' - функция, преобразующая строку s к вещественному; может генерировать исключение
| |
− |
| |
− | <H3>Некоторые задачи на строки</H3>
| |
− |
| |
− | Посимвольная обработка
| |
− |
| |
− | * Строка 'ABCD...XYZ'
| |
− | * Сумма всех цифр в строке s
| |
− |
| |
− | Использование стандартных подпрограмм
| |
− |
| |
− | * Вывести индексы всех вхождений подстроки s1 в s (на Delete, PosEx)
| |
− |
| |
− | ==Ссылки==
| |
− | *[[Основы программирования — Осенний семестр; Михалкович С.С.; 2008; I| Первая часть конспекта]]
| |
− | *[[Конспекты|Другие конспекты]]
| |
<xh4> Семанитика </xh4>
Вычисляется выражение в правой части, при этом, вместо имен переменных подставляются их значения.
Затем результат вычисления записывается в переменную в левой части.
Это стандартное решение.
В PascalABC.NET на основе этого алгоритма определена стандартная процедура Swap(x, y).
Однако, существуют и другие решения. Например:
Заметим, что в первом решении используется 6 операций умножения, в во 2м и 3м — 5. Возникает задача: найти xn за минимальное число умножений.
Об этом читай тему.
<xh4> Семантика </xh4>
Происходит считывание данных с клавиатуры и запись их в переменные из <списка переменных>.
Вводить данные нужно либо через пробел, либо по нажатию <Enter>, при этом программа не перейдет к выполнению следующего оператора, пока не будут считаны все данные.
Имеются также стандартные функции ReadInteger, ReadReal, ReadlnInteger, ReadlnReal:
Операторы, которые могут получать ошибку, заключаются специальный охранный блок - оператор try.
<xh4> Семантика </xh4>
Выражения в списке вычисляются, и их значения выводятся на экран.
В случае writeln после вывода осуществляется переход на новую строку.
<xh4> Форматы вывода </xh4>
После каждого выражения в списке вывода можно использовать формат вывода в виде :a, где a — выражение целого типа.
После вещественного типа — :a:b (a задает ширину поля вывода (выравнивание по правому краю), b — количество знаков в дробной части).
В форматной строке тоже можно использовать формат вывода.
{0, 10}: 10 — это ширина поля вывода
{0, 10:f3}: 3 — это количество знаков в дробной части для вещественного числа (показывает это спецификатор f).
{0, 10:e3} — экспоненциальный формат.
Это простые логические выражения. Однако, с помщью логических операций можно составлять сложные.
<xh4> Сокращение вычислений логических выражений </xh4>
Большинство современных компиляторов, в т.ч. PascalABC.NET производит сокращенное вычисление логических выражений.
Это означает, что в выражении
Логически здесь все верно, однако, если бы не использовалось сокращенное вычисление, в случае равенства нулю y'а возникала бы ошибка деления на ноль.
Соответствующая операция применяется к каждому биту двоичного представления числа.
Пример.
При a<b возможны три варианта, в двух из которых a<c.
При a>b возможны три варианта, в двух из которых a>c.
Решение представляется вложенными операторами if с уровнем вложенности 3.
По количеству операций (3 сравнения, одно присваивание) это - самое лучшее решение
Данное решение менее эффективно по числу сравнений и присваиваний (посчитайте самостоятельно), но по понятности может восприниматься лучше предыдущего.
НО НЕ строковый или вещественный.