Основы программирования — Осенний семестр; Михалкович С.С.; 2008; II — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Устройство ЗА подпрограммы)
(Координаты средней точки)
 
(не показаны 64 промежуточные версии 6 участников)
Строка 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>
 
==Область видимости и время жизни объекта==
 
'''Время жизни переменной''' — время, в течение которого существует отведенная ей память.
 
*для глобальных переменных это время работы программы
 
*для локальных — с момента описания до конца работы блока, где они описаны
 
 
 
'''Область видимости переменной''' — область в программе, в которой можно обратиться к переменной по имени.
 
==Побочный эффект==
 
'''Побочным эффктом подпрограммы''' называется также любое изменение глобальных переменных (или параметров для функции).
 
 
 
'''Пример.'''
 
<source lang="Pascal">var n: integer;
 
procedure p;
 
begin
 
  n += 1;
 
end;
 
 
 
begin
 
  n := 5;
 
  p;
 
  write(p);
 
end.</source>
 
 
 
'''Замечание.''' Если подпрограмма желает изменить глобальную переменную, то в большинстве случаев эту переменную следует передать как параметр.
 
 
 
==Принцип локальности==
 
Рассмотрим пример:
 
<source lang="Pascal">var n: integer;
 
 
 
procedure p;
 
begin
 
  <много кода; n не используется>
 
end;
 
 
 
procedure p1;
 
begin
 
  <еще больше кода; n не используется>
 
end;
 
 
 
begin
 
  <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.''' Тип возвращаемого значения функции не участвует в операции разрешения перегрузки.
+
Заметим, что в первом решении используется 6 операций умножения, в во 2м и 3м — 5. Возникает задача: '''''найти x<sup>n</sup> за минимальное число умножений.''''' <br />
 +
Об этом читай [http://it.mmcs.sfedu.ru/forum?func=view&id=22350&catid=1 тему].
  
Версии подпрограммы с одним именем называются '''перегруженными''', а определение того, какую версию выбрать — '''разрешением перегрузки'''.
+
=== Оператор ввода ===
 +
<xh4> Синтаксис </xh4>
 +
'''read''' (<список переменных>) | '''readln''' (<список переменных>)
  
'''Замечание 2.''' Разрешить перегрузку можно не всегда.
+
<xh4> Семантика </xh4>
<br>'''Пример.'''
+
Происходит считывание данных с клавиатуры и запись их в переменные из <tt><списка переменных></tt>.
<source lang="Pascal">procedure p(i: integer; r: real);
+
Вводить данные нужно либо через пробел, либо по нажатию <tt><Enter></tt>, при этом программа не перейдет к выполнению следующего оператора, пока не будут считаны все данные.
...
 
 
 
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>
 
'''Параметры по умолчанию''' обязательно должны находиться последними в списке параметров. Если при вызове не указывать этот параметр, будет использовано значение по умолчанию.
 
  
'''Замечание.''' Параметры по умолчанию корректно сочетаются с перегрузкой.
+
Имеются также стандартные функции ReadInteger, ReadReal, ReadlnInteger, ReadlnReal:
==Предварительное объявление подпрограмм==
 
<source lang="Pascal">procedure p;
 
begin
 
  q;
 
end;
 
  
procedure q;
+
<source lang="Delphi">var n := ReadInteger;
begin
+
var r := ReadlnReal;</source>
  ...
 
end;</source>
 
Такое описание вызовет ошибку компиляции, т.к. в '''<tt>p</tt>''' используется еще не известная процедура. Чтобы таких ошибок не возникало, нужно использовать ''предварительное объявление подпрограммы'':
 
<source lang="Pascal">procedure q; forward;
 
  
procedure p;
+
С процедурой ввода связан ряд '''''ошибок''''' времени выполнения (например, если переменная используется в качестве делителя, и вводится 0, или, если должно быть получено целое число, а вводится <tt>'ABC'</tt>). Эти ошибки нужно уметь обрабатывать.
begin
 
  q;
 
end;
 
  
procedure q;
+
=== Оператор try/except и обработка ошибок ввода ===
  ...</source>
+
Операторы, которые могут получать ошибку, заключаются специальный охранный блок - оператор <tt>'''try'''</tt>.
'''forward-объявление''' делается для подпрограмм, которые ''обязательно'' будут описаны ниже. Если программа не будет описана в этом же файле, то, в конце компиляции этого файла, мы получим ошибку компилятора о <tt>'''forward'''</tt>, который был объявлен, но не описан.
 
  
==Процедурные переменные==
+
<xh4> Синтаксис </xh4>
===Процедурный тип и переменные===
 
<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
 
 
   ...
 
   ...
 +
except
 +
  <обработка ошибки>
 
end;
 
end;
procedure Sleep;
+
<продолжение работы>
begin
+
</source>
  ...
 
end;
 
 
 
var pr: ActionType;
 
begin
 
  pr += Sleep;
 
  pr += Eat;
 
  pr += Think;
 
  pr;
 
  pr -= Eat;
 
  pr;
 
end.</source>
 
'''Замечание 1.''' Такое возможно только в .NET;
 
<br>'''Замечание 2.''' Подобный механизм не имеет смысла использовать для функций;
 
  
<small>'''Лекция 12'''</small>
+
<xh4> Семантика </xh4>
==Методы разработки подпрограмм==
+
Если внутри блока '''<tt>try</tt>''' происходит ''ошибка выполнения'', то все последующие операторы в блоке игнорируются, и выполнение программы переходит к блоку '''<tt>except</tt>'''. По выходе из '''<tt>except</tt>''' программа продолжает работу.
'''Пример.''' Вывести таблицу простых чисел <= 1000.
 
===Метод разработки ''сверху вниз''===
 
Метод разработки '''сверху вниз''' начинается с записи основного алгоритма в предположении,
 
что все подпрограммы уже написаны, т.е. код пишется ''в крупных командах''.
 
  
'''Решение.'''
+
Если ошибки не происходит, то выполняются все операторы в блоке '''<tt>try</tt>''', блок '''<tt>except</tt>''' не выполняется, и программа продолжает работу.
<source lang="Pascal">writeln(2);
 
var x := 3;
 
while x <= 1000 do
 
begin
 
  if IsPrime(x) then
 
    writeln(x);
 
  x += 2;
 
end;</source>
 
После этого реализуем функцию IsPrime;
 
  
Этот метод используется, когда:
+
=== Оператор вывода ===
*основной алгоритм понят, и его можно сразу записать
+
<xh4> Синтаксис </xh4>
*задача четко поставлена
+
'''write'''(<список выражений>) | '''writeln'''(<список выражений>)
*имеется ровно одна задача, которую необходимо решить
 
  
'''Замечание.''' В коде главной программы имеются вызовы других подпрограмм, которые реализуются позже (возможно, другими разработчиками);
+
<xh4> Семантика </xh4>
<br>На период, пока они не написаны, вместо них могут использоваться "'''заглушки'''" (подпрограммы с тривиальными телами)
+
Выражения в списке вычисляются, и их значения выводятся на экран. <br />
 +
В случае <tt>writeln</tt> после вывода осуществляется переход на новую строку.
  
Код каждой разрабатываемой подпрограммы также может разрабатываться методом сверху вниз.
+
<xh4> Форматы вывода </xh4>
===Метод разработки ''снизу вверх''===
+
После каждого выражения в списке вывода можно использовать '''формат вывода''' в виде <tt>''':a'''</tt>, где <tt>'''a'''</tt> — выражение целого типа.<br />
Вначале составляется "нижняя" подпрограмма, она всесторонне тестируется, и, потом,
+
После вещественного типа — <tt>''':a:b'''</tt> (<tt>'''a'''</tt> задает ширину поля вывода (выравнивание по правому краю), <tt>'''b'''</tt> — количество знаков в дробной части).
пишется основная программа или подпрограмма.
 
  
Метод используется, когда:
+
<xh4> Вывод с помощью write[ln]Format </xh4>
*на момент начала решения задача нечетко поставлена
+
'''writelnFormat'''('<форматная строка>', <список выражений>)
*когда задача — большая
 
*нет ясно выраженного главного алгоритма, а есть множество взаимосвязанных задач (примером может служить программа, реализующая деятельность университета)
 
  
'''Замечание 1.''' Написание самых "нижних" подпрограмм позволяет лучше войти в предметную область;
+
''<u>Пример</u> вывода с использованием форматной строки''.
<br>'''Замечание 2.''' С какого-то момента разработка хотя бы упрощенного главного алгоритма становится необходимой;
+
<source lang="Pascal">
 +
writelnFormat('{0} * {1} = {2}', a, b, a * b)
 +
</source>
  
Если между главным алгоритмом и "нижними" подпрограммами имеется несколько уровней,
+
Будет выведено:
то имеет смысл '''сочетать оба метода разработки'''.
+
  a * b = '''a * b'''
==Оператор goto==
 
===Синтаксис===
 
'''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:
+
<br><tt>{0, 10}</tt>: 10 — это ширина поля вывода
:a<sup>2</sup> + b<sup>2</sup> = c<sup>2</sup>, 50 <= a,b,c <= 100
+
<br><tt>{0, 10:f3}</tt>: 3 — это количество знаков в дробной части для вещественного числа (показывает это спецификатор <tt>'''f'''</tt>).
<source lang="Pascal">label fin;
+
<br><tt>{0, 10:e3}</tt> — экспоненциальный формат.
var aa, bb, cc: integer;
 
var flag := false;
 
  
for var a:=50 to 100 do
+
=== Арифметические выражения ===
for var b:=50 to 100 do
+
==== Основные сведения ====
for var c:=50 to 100 do
+
Каждое выражение имеет '''тип'''. Выражение называется '''''арифметическим''''', если его тип — ''числовой''. <br />
  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> — одного типа, то и <tt>a '''op''' b</tt> принадлежит к тому же типу. ''Исключением'' является операция "'''/'''":
      writeln(aa, bb, cc);</source>
+
:<tt>'''a / b'''</tt> — вещественное.
== Вызов подпрограммы на этапе выполнения ==
 
С каждой выполняющейся программой связан блок памяти, выделяющийся ей в начале работы и называемый '''программным стеком'''.
 
<br>Программный стек используется для хранения значений всех переменных, констант, а также промежуточных значений в процессе выполнения.
 
<здесь будет рисунок>
 
При в ходе в подпрограмму в процессе выполнения, на программном стеке для нее выделяется специальный участок памяти, называемый '''записью активации''' ('''ЗА''') этой подпрограммы.
 
  
Данный участок выделяется на вершине стека, после чего вершина стека <tt>'''top'''</tt> "поднимается на размер ЗА.
+
Если <tt>a</tt> и <tt>b</tt> принадлежат к различным типам, то выражение принадлежит к "'''''старшему'''''" типу. <br />
<br>После окончания работы подпрограммы ее ЗА снимается со стека и вызывающая программа выполняется дальше.
+
Например:
 +
byte < integer < int64
 +
integer < real
  
'''Пример.'''
+
==== Стандартные функции ====
<br>[[Изображение:Работа_стека.gif]]
+
В арифметические выражения могут входить стандартные функции:
=== Устройство ЗА подпрограммы ===
+
'''exp'''(x)
[[Изображение:ЗА.png]]
+
'''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 <> <->   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| Первая часть конспекта]]
 
*[[Конспекты|Другие конспекты]]
 

Текущая версия на 11:21, 14 сентября 2013

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

Основные операторы

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

<xh4> Синтаксис </xh4>

<переменная> := <выражение>

Пример использования оператора присваивания.

a := (3 + 5) * 8; 
b := a + 2;

<xh4> Семанитика </xh4> Вычисляется выражение в правой части, при этом, вместо имен переменных подставляются их значения.
Затем результат вычисления записывается в переменную в левой части.

Ограничение. Тип выражения должен быть совместим по присваиванию с переменной.
Например:

  • одинаковые типы совместимы.
  • выражение типа integer можно присвоить переменной типа real. Обратное неверно.

<xh4> Операторы присваивания += и *= </xh4> Пример.

d += 1; //прибавить 1 к d
d *= 2; //умножить d на 2

<xh4> Примеры использования := </xh4> Пример 1. Перемена местами двух значений. Дано: x, y;

var x, y: integer;
begin
  read(x,y);
  var v := x;
  x := y;
  y := v;
  writeln(x, ' ', y);
end.

Это стандартное решение. В PascalABC.NET на основе этого алгоритма определена стандартная процедура Swap(x, y).

Однако, существуют и другие решения. Например:

var x, y: integer;
begin
  read(x, y);
  x := x + y;
  y := x - y;
  x := x - y;
  writeln (x, ' ', y);
end.

Пример 2. Использование промежуточных переменных в вычислениях Дано: x: real; Найти: x15;

Решение 1.

y := x * x;
z := y * y;
t := z * z;
p := t * z;
q := p * x * y;

Решение 2.

y := x * x;
z := y * x;
t := z * y;
p := t * t * t;

Решение 3.

y := x * x;
x := x * y * y;
t := x * x * x;

Заметим, что в первом решении используется 6 операций умножения, в во 2м и 3м — 5. Возникает задача: найти xn за минимальное число умножений.
Об этом читай тему.

Оператор ввода

<xh4> Синтаксис </xh4>

read (<список переменных>) | readln (<список переменных>)

<xh4> Семантика </xh4> Происходит считывание данных с клавиатуры и запись их в переменные из <списка переменных>. Вводить данные нужно либо через пробел, либо по нажатию <Enter>, при этом программа не перейдет к выполнению следующего оператора, пока не будут считаны все данные.

Имеются также стандартные функции ReadInteger, ReadReal, ReadlnInteger, ReadlnReal:

var n := ReadInteger;
var r := ReadlnReal;

С процедурой ввода связан ряд ошибок времени выполнения (например, если переменная используется в качестве делителя, и вводится 0, или, если должно быть получено целое число, а вводится 'ABC'). Эти ошибки нужно уметь обрабатывать.

Оператор try/except и обработка ошибок ввода

Операторы, которые могут получать ошибку, заключаются специальный охранный блок - оператор try.

<xh4> Синтаксис </xh4>

try
  ...
  readln(a);
  ...
except
  <обработка ошибки>
end;
<продолжение работы>

<xh4> Семантика </xh4> Если внутри блока try происходит ошибка выполнения, то все последующие операторы в блоке игнорируются, и выполнение программы переходит к блоку except. По выходе из except программа продолжает работу.

Если ошибки не происходит, то выполняются все операторы в блоке try, блок except не выполняется, и программа продолжает работу.

Оператор вывода

<xh4> Синтаксис </xh4>

write(<список выражений>) | writeln(<список выражений>)

<xh4> Семантика </xh4> Выражения в списке вычисляются, и их значения выводятся на экран.
В случае writeln после вывода осуществляется переход на новую строку.

<xh4> Форматы вывода </xh4> После каждого выражения в списке вывода можно использовать формат вывода в виде :a, где a — выражение целого типа.
После вещественного типа — :a:b (a задает ширину поля вывода (выравнивание по правому краю), b — количество знаков в дробной части).

<xh4> Вывод с помощью write[ln]Format </xh4>

writelnFormat('<форматная строка>', <список выражений>)

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

writelnFormat('{0} * {1} = {2}', a, b, a * b)

Будет выведено:

a * b = a * b

В форматной строке тоже можно использовать формат вывода.
{0, 10}: 10 — это ширина поля вывода
{0, 10:f3}: 3 — это количество знаков в дробной части для вещественного числа (показывает это спецификатор f).
{0, 10:e3} — экспоненциальный формат.

Арифметические выражения

Основные сведения

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

В арифметических выражениях если a и b — одного типа, то и a op b принадлежит к тому же типу. Исключением является операция "/":

a / b — вещественное.

Если a и b принадлежат к различным типам, то выражение принадлежит к "старшему" типу.
Например:

byte < integer < int64
integer < real

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

В арифметические выражения могут входить стандартные функции:

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>

  • Операции с большим приоритетом выполняются первыми
  • Функции вычисляются до операций
  • Выражение в скобках вычисляется раньше
  • Операции с одинаковым приоритетом выполняются слева направо, если идут подряд.

<xh4> Операции div и mod для целых </xh4> x div y = x / y, округленное до ближайшего целого по направлению к нулю. Это результат от целочисленного деления.
x mod y = x - (x div y) * y. Это остаток от целочисленного деления.

Пример использования
Целочисленные операции часто применяются для определения четности числа:

x mod 2 = 0    <->   x — четное
x mod 2 <> 0   <->   x — нечетное

Логические выражения

<xh4> Основные сведения </xh4> Выражение назывется логическим, если оно имеет тип boolean.
Пример.

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 производит сокращенное вычисление логических выражений.
Это означает, что в выражении

a and b

если a — ложно, то b не вычисляется, а в

a or b

если a — истинно, b не вычисляется.

Это очень полезно при вычислении таких выражений, как, например,

(y <> 0) and (x / y > 0)

Логически здесь все верно, однако, если бы не использовалось сокращенное вычисление, в случае равенства нулю y'а возникала бы ошибка деления на ноль.

<xh4> Логические переменные </xh4> Можно описывать логические переменные (тип boolean). Им можно присваивать логические выражения.
Эти переменные принимают одно из двух возможных значений:

true (истина)
false (ложь)

Пример использования логических переменных
Дано: прямоугольник со сторонами, параллельными осям координат, задан координатами абсцисс вертикальных сторон (x1, x2) и ординатами горизонтальных (y1, y2); точка M( x, y );
Найти: находится ли точка внутри прямоугольника, снаружи, или лежит на границе;

var inside, outside, bound: boolean;
begin
  inside := (x > x1) and (x < x2) and (y > y1) and (y < y2);
  outside := (x < x1) or (x > x2) or (y < y1) or (y > y2);
  bound := not inside and not outside;
end.

Побитовые операции

<xh4> Побитовые операции and, or, xor, not </xh4> Замечание. Работают только с целыми.

Соответствующая операция применяется к каждому биту двоичного представления числа.
Пример.

5 and 7

510 = 1012
710 = 1112

101
   ( and )
111
———
1012 = 510

<xh4> Операции shl и shr </xh4> Побитовый сдвиг влево и сдвиг вправо соответственно.

<xh4> shl </xh4>

x shl n = x * 2n

Сдвигает двоичное представление x на n позиций влево.

<xh4> shr </xh4>

x shr n = x div 2n

Сдвигает двоичное представление x на n позиций вправо.

<xh4> Примеры </xh4>

x = 510 = 1012

x shl 2 = <—(2)101
             101002 = 2010

x shr 2 = 101—>(2)
          0012 = 110

Таблица приоритетов операций языка Object Pascal

  1. унарные + - not
  2. имеющие смысл умножения * / div mod and shl shr
  3. имеющие смысл сложения + - or xor
  4. операции отношения <> <= >= < > in

Условный оператор

<xh4> Синтаксис </xh4>

if <условие> then <оператор1>
             [else <оператор2>]

<xh4> Семантика </xh4> If.jpg

Примеры использования для решения задач

Пример 1. Нахождение минимума
Дано: x, y
Найти: min

if x > y then
  min := y
else
  min := x;

Пример 2. Упорядочение a, b по возрастанию.
Ясно, что если a > b, — нужно поменять их местами.
Но тут одним оператором не обойтись. Для этого можно использовать составной оператор — один или больше операторов, заключенных в операторные скобки begin - end;:

if a > b then
begin
  var v := b;
  b := a;
  a := v;
end;

Пример 3. Вычисление функции по взаимоисключающим веткам
<math>y = \begin{cases} x, & x < 2 \\ x^2, & 2 < x < 3 \\ 1-x, & x \ge\; 3 \end{cases}</math>

if x < 2 then
  y := x
else if x < 3 then
  y := x * x
else y := 1 - x;

Замечание. Если по ветви else располагается другой оператор if, то говорят, что возникает цепочка вложенных операторов if.

Координаты средней точки

Пример 4. Даны координаты трех точек на прямой a, b, c (a, b, c попарно не равны). Найти m - координаты средней точки

Решение 1.

Достаточно рассмотреть случай, когда координаты точек равны 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.

Решение представляется вложенными операторами if с уровнем вложенности 3.

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
      m := c
    else
      m := b
  else m := a;

По количеству операций (3 сравнения, одно присваивание) это - самое лучшее решение

То же решение, записанное с помощью функции min:

if a < b then
  if a < c then
    m := min(b,c)
  else m := a
else
  if a > c then
    m := min(b,c)
  else m := a;

Решение 2.

var m1 := min(a,b);
var m2 := max(a,b);
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
var m1 := min(a,b);
var m2 := max(a,b);
if c<m1 then
  m := m1
else if c>m2 then
  m := m2
else m := c;

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

Самостоятельные задания

  • Даны координаты вершин треугольника и точка M, не лежащая на границе треугольника. Принадлежит ли M треугольнику.
  • Является ли 4-угольник ABCD корректно заданным.

Оператор case выбора варианта

<xh4> Синтакстис </xh4>

case <переключатель> of
  {<список выбора>: <оператор>;}
  [else <оператор>[;]]
end

<xh4> Семантика </xh4> Вначале вычисляется выражение-<переключатель>, после чего его значение ищется в одном из <списков выбора>.
Если значение попадает в какой-то <список выбора>, то выполняется соответствующий ему оператор, иначе, если есть ветвь else, то выполняется оператор по ветке else.

<xh4> Ограничения </xh4>

  • выражение-переключатель должно иметь так называемый порядковый тип:
целый
символьный
перечислимый

НО НЕ строковый или вещественный.

  • значения в <списках выбора> не должны пересекаться.

<xh4> Примеры использования оператора выбора </xh4> Пример 1. День недели

case DayOfWeek of
  1..5: writeln('Будний');
  6, 7: writeln('Выходный');
  else  writeln('Ошибка');
end;

Пример 2. Цифра или буква

var c: char;
read(c);
case c of
  '0'..'9': writeln('Цифра');
  'A'..'Z', 'a'..'z', 'а'..'я', 'А'..'Я', 'ё', 'Ё': writeln('Буква');
end;