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

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Записи)
Строка 1: Строка 1:
 
[[Категория:Конспекты]]
 
[[Категория:Конспекты]]
==Подпрограммы==
+
==Оператор присваивания := ==
===Вспомогательные алгоритмы===
 
'''Вспомогательный алгоритм''' — это алгоритм, который используется для реализации другого алгоритма.
 
  
Вспомогательные алгоритмы имеют:
+
<h3>Синтаксис</h3>
*'''''имя'''''
+
<переменная> ''':=''' <выражение>
*'''''список параметров'''''
 
:некоторые параметры являются ''входными'', а некоторые — ''выходными''
 
  
Если один из ''выходных'' параметров возвращается особым образом, так что алгоритм можно использовать в выражении, то такой алгоритм называется '''алгоритмом-функцией'''.
+
'''Пример использования оператора присваивания'''
 +
a ''':=''' (3 + 5) * 8;
 +
b := a + 2;
  
В программировании вспомогательные алгоритмы называются '''подпрограммами''' и делятся на:
+
<h3>Семанитика</h3>
*'''''процедуры'''''
+
Вычисляется выражение в правой части, при этом вместо имен переменных подставляются их значения.
*'''''функции'''''
+
Затем результат вычисления записывается в переменную в левой части.
  
 +
'''Ограничение.''' Тип выражения должен быть ''совместим по присваиванию'' с переменной.
 +
Например:
 +
*одинаковые типы совместимы.
 +
*выражение типа integer можно присвоить переменной типа real, обратное неверно.
  
'''Важно.''' Подпрограммы позволяют избежать дублирования кода при решении однотипных задач.
+
<h3>Операторы присваивания += и *=</h3>
Алгоритм один раз описывается, а затем может быть многократно вызван с различным набором параметров из другого алгоритма.
 
  
<small>'''Лекция 9'''</small>
 
 
===Процедуры===
 
 
'''Пример.'''
 
'''Пример.'''
:Даны 3 пары положительных чисел.
+
d += 1; //прибавить 1 к d
:Найти их среднее арифметическое и среднее геометрическое.
+
d *= 2; //умножить d на 2
Очевидно, удобно сделать подпрограмму, которой бы на вход подавались два числа, а выходными параметрами являлись их среднее арифметическое и среднее геометрическое соответственно.
 
<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>
 
называется '''заголовком процедуры'''.
 
  
Теперь можем многократно использовать описанную процедуру в основной программе:
+
<small>'''Лекция 3'''</small>
<source lang="Pascal">begin  //основной программы
+
<h3>Примеры использования :=</h3>
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>
+
'''Пример 1.''' Перемена местами двух значений.
'''procedure''' <имя> [( <список формальных параметров> )];
+
:Дано:  x,y;
<раздел описаний>
+
   <source lang="Pascal">
'''begin'''
+
var x,y: integer;
  <операторы и внутриблочные переменные>
 
'''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 на основе этого алгоритма определена стандартная процедура Swap(x, y).
  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>
 
называется '''заголовком функции''', а <tt>integer</tt> — это '''тип возвращаемого значения'''.
 
  
В каждой функции неявно определена переменная <tt>'''result'''</tt>, хранящая возвращаемое значение функции и имеющая тот же тип.
+
'''Пример 2.''' Использование промежуточных переменных в вычислениях
Присваивание значения Result в теле функции ''обязательно по всем ветвям'' алгоритма.
+
:Дано:  x: real;
 +
:Найти:  x<sup>15</sup>;
  
'''Пример ''неправильной (!!!)''''' функции.
+
'''Решение 1.'''
<source lang="Pascal">function f(x: integer): integer;
+
<source lang="Pascal">
begin
+
y := x * x;
  if x > 0 then
+
z := y * y;
    Result := 777;
+
t := z * z;
end;
+
p := t * z;
 +
q := p * x * y;
 +
</source>
 +
'''Решение 2.'''
 +
<source lang="Pascal">
 +
y := x * x;
 +
z := y * x;
 +
t := z * y;
 +
p := t * t * t;
 +
</source>
 +
'''Решение 3.'''
 +
<source lang="Pascal">
 +
y := x * x;
 +
x := x * y * y;
 +
t := x * x * x;
 +
</source>
  
begin
+
Заметим, что в первом решении используется 6 операций умножения, в во 2м и 3м — 5. Возникает задача:
  writeln(f(0));
+
:'''''найти x<sup>n</sup> за минимальное число умножений.'''''
end.</source>
+
:Об этом читай [http://it.mmcs.sfedu.ru/forum?func=view&id=22350&catid=1 тему].
Выведется непредсказуемое значение, находящееся в этот момент в памяти, т.к. по ветке x = 0 переменная Result не инициализируется.
 
  
'''Пример.''' Считывание значения с клавиатуры.
+
==Оператор ввода==
<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>
 
'''Замечание.''' Формальные параметры функции ''не'' рекомендуется делать '''параметрами-переменными'''.
 
  
Считается, что функция должна вычислять и возвращаеть результат, и более ничего не делать.
+
<h3>Синтаксис</h3>
Если функция возвращает результат и выполняет дополнительное действие, которое сказывается вне функции, то такие действия называются '''побочным рузультатом вызова функции.'''
+
'''read''' (<список переменных>) | '''readln''' (<список переменных>)
  
'''Правило.'''
+
<h3>Семантика</h3>
:В подавляющем большинстве случаев функция не должна иметь побочный эффект.
+
Происходит считывание данных с клавиатуры и запись их в переменные из <списка переменных>. Вводить данные нужно либо через пробел, либо по нажатию <Enter>, при этом программа не перейдет к выполнению следующего оператора, пока не будут считаны все данные.
  
===Оператор exit===
+
С процедурой ввода связан ряд ''ошибок'' (например, если переменная используется в качестве делителя и вводится 0, или если должно быть получено целое число, а вводится 'ABC'). Эти ошибки нужно уметь обрабатывать.
Оператор '''<tt>exit</tt>''' — оператор досрочного завершения подпрограммы.
 
  
'''Пример'''. Поиск числа k среди n введенных.
+
==Оператор try/except и обработка ошибок ввода==
<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>, позволяет завершить подпрограмму и выйти сразу из нескольких вложенных циклов. При вызове его в теле основной программы программа завершается.
 
  
==Локальные и глобальные переменные==
+
Операторы, которые могут получать ошибку, заключаются специальный охранный блок - оператор <tt>'''try'''</tt>.
<source lang="Pascal">var
 
  n: integer;
 
  m: integer;
 
  
function f(x: integer): integer;
+
<h3>Синтаксис</h3>
var n: integer;
+
<source lang="Pascal">
begin
+
try
   writeln(n);
+
  ...
   writeln(m);
+
   readln(a);
   n := 5;
+
   ...
end;</source>
+
except
'''Замечание.''' '''Локальная''' переменная, описанная в процедуре или функции, может иметь то же имя, что и '''глобальная''' переменная.
+
   <обработка ошибки>
Если глобальная описана ранее, то её имя внутри процедуры становится недоступным.
+
end;
 +
<продолжение работы>
 +
</source>
  
Глобальные переменныя по умолчанию инициализируются '''0''', а локальные по умолчанию не инициализируются.
+
<h3>Семантика</h3>
Локальная переменная <tt>'''Result'''</tt> в функции по умолчанию также не инициализируется.
 
  
В данном примере:
+
Если внутри блока '''try''' происходит ошибка выполнения, то все последующие операторы в блоке игнорируются и выполнение программы переходит к блоку '''except'''. По выходе из '''except''' программа продолжает работу.
:writeln(n) выведет непредсказуемый результат;
 
:writeln(m) выведет 0;
 
  
==Пространство имен==
+
Если ошибки не происходит, то выполняются все операторы в блоке '''try''', блок '''except''' не выполняется и программа продолжает работу.
Область программы, в которой не может быть описано двух объектов с одинаковыми именем, называется '''пространством имен'''.
 
  
Различают:
+
==Оператор вывода==
*'''''глобальное''''' пространство имен
+
<h3>Синтаксис</h3>
*пространство имен '''''каждой п/п'''''
+
'''write'''( <список выражений> ) | '''writeln'''( <список выражений> )
  
''Формальные'' параметры п/п являются ''локальными'' по отношению к эти п/п.
+
<h3>Семантика</h3>
 +
Выражения в списке вычисляются и тих значения выводятся на экран.
 +
В случае <tt>writeln</tt> после выводао существляется переход на новую строку.
  
'''Примеры''' допустимых и недопустимых описаний:
+
<h3>Форматы вывода</h3>
'''procedure''' p(p: integer);        //Можно
+
После каждого выражения в списке вывода можно использовать формат вывода в виде <tt>''':a'''</tt>, где a — выражение целого типа.
+
После вещественного типа — <tt>''':a:b'''</tt>
'''function''' Result: integer;      //Можно
 
 
'''procedure''' p(i: integer);        //НЕЛЬЗЯ
 
'''var''' i: integer;
 
 
function f(Result: real): real; //НЕЛЬЗЯ
 
<small>'''Лекция 10'''</small>
 
==Область видимости и время жизни объекта==
 
'''Время жизни переменной''' — время, в течение которого существует отведенная ей память.
 
*для глобальных переменных это время работы программы
 
*для локальных — с момента описания до конца работы блока, где они описаны
 
  
'''Область видимости переменной''' — область в программе, в которой можно обратиться к переменной по имени.
+
<tt>'''a'''</tt> задает ширину поля вывода ( выравнивание по правому краю ).
==Побочный эффект==
+
<br><tt>'''b'''</tt> — количество знаков в дробной части.
'''Побочным эффктом подпрограммы''' называется также любое изменение глобальных переменных (или параметров для функции).
 
  
'''Пример.'''
+
<h3>Вывод с помощью write[ln]Format</h3>
<source lang="Pascal">var n: integer;
+
'''writelnFormat'''( '<форматная строка>', <список выражений> )
procedure p;
 
begin
 
  n += 1;
 
end;
 
  
begin
+
'''Пример''' вывода с использованием форматной строки
  n := 5;
+
<source lang="Pascal">writelnFormat('{0} * {1} = {2}', a, b, a * b);</source>
  p;
+
Будет выведено:
  write(p);
+
a * b = '''a * b'''
end.</source>
 
  
'''Замечание.''' Если подпрограмма желает изменить глобальную переменную, то в большинстве случаев эту переменную следует передать как параметр.
+
В форматной строке тоже можно использовать формат вывода.
 +
<br><tt>{0, 10}</tt> — 10 это ширина поля вывода
 +
<br><tt>{0, 10:f3}</tt> — 3 это количество знаков в дробной части для вещественного числа (показывает это спецификатор f).
  
==Принцип локальности==
+
==Условный оператор==
Рассмотрим пример:
+
<h3>Синтаксис</h3>
<source lang="Pascal">var n: integer;
+
'''if''' <условие> '''then''' <оператор<sub>1</sub>>
 +
              '''else''' <оператор<sub>2</sub>>
  
procedure p;
+
<h3>Семантика</h3>
begin
+
[[Изображение: If.jpg]]
  <много кода; n не используется>
+
<h3>Примеры использования для решения задач</h3>
end;
 
  
procedure p1;
+
'''Пример 1.''' Нахождение минимума
 +
<br>Дано: x,y; <br> Найти: min;
 +
<source lang="Pascal">if x > y then
 +
  min := y
 +
else
 +
  min := x;</source>
 +
'''Пример 2.''' Упорядочение a, b по возрастанию
 +
<br>Ясно, что если a>b, — нужно [http://it.mmcs.rsu.ru/wiki/index.php/%D0%9A%D0%BE%D0%BD%D1%81%D0%BF%D0%B5%D0%BA%D1%82_%D0%BB%D0%B5%D0%BA%D1%86%D0%B8%D0%B9_%D0%BA%D1%83%D1%80%D1%81%D0%B0_%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_1_%D1%81%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D1%80_%D0%98%D0%A2#.D0.9F.D1.80.D0.B8.D0.BC.D0.B5.D1.80.D1.8B_.D0.B8.D1.81.D0.BF.D0.BE.D0.BB.D1.8C.D0.B7.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D1.8F_:.3D поменять их местами]. <br>Но тут одним оператором не обойтись.
 +
Для этого можно использовать '''''составной оператор''''' — один или больше операторов, заключенных в операторные скобки <tt>'''begin'''  - '''end''';</tt>:
 +
<source lang="Pascal">if a > b then
 
begin
 
begin
   <еще больше кода; n не используется>
+
   var v := b;
 +
  b := a;
 +
  a := v;
 
end;
 
end;
 +
</source>
  
begin
+
'''Пример 3.''' Вычисление функции по взаимоисключающим веткам
  <10 операторов>
+
<br><math> y = \begin{cases} x, & x < 2 \\ x^2, & 2 < x < 3 \\ 1-x, & x \ge\; 3 \end{cases}</math>
   n := 1;
+
<source lang="Pascal">if x < 2 then
   <еще операторы>
+
   y := x
end.</source>
+
else
'''Принцип локальности''' — эмпирический принцип, по которому переменную следует описывать и впервые инициализировать в том месте алгоритма, где она начинает впервые использоваться.
+
   if x < 3 then
 +
    y := x * x
 +
  else
 +
    y := 1 - x;</source>
  
Обычно входные и выходные данные описываются, как глобальные переменные, после всех подпрограмм (если эти подпрограммы их не используют).
+
'''Замечание.''' Если по ветви '''<tt>else</tt>''' располагается другой оператор '''<tt>if</tt>''', то говорят, что возникает '''''цепочка вложенных операторов''''' '''<tt>if</tt>'''.
В начале текста программы обычно используются переменные и константы, инициализируемые каким-то значением, которое легко поменять при следующих запусках этой подпрограммы.
 
  
'''Пример.''' Вычислить произведениe целых чисел от a до b.
+
'''Пример 4.''' Найти среднее среди a,b,c (a,b,c попарно не равны)
'''Решение 1.'''
+
Эта задача имеет несколько вариантов решения.
<source lang="Pascal">var
+
<source lang="Pascal">if a < b then
   p := 1;
+
   if a < c then
  a, b: integer;
+
    if b < c then
   i: integer;
+
      sr := b
begin
+
    else
   read(a, b);
+
      sr := c
  for i:=a to b do
+
   else
     p *= i;
+
    sr := a
   writeln(p);
+
else
  writeln(i); //от компилятора зависит, будет i = b или (b + 1)
+
   if a > c then
end.</source>
+
    if b > c then
Внесем изменения:
+
      sr := b
*опишем i в заголовке цикла
+
     else
:при описании переменной в заголовке цикла её область видимости и время жизни ограничены телом цикла
+
      sr := c
*инициализируем p непосредственно перед циклом, вычисляющим произведение
+
   else sr := a;</source>
'''Решение 2.'''
+
Очевидно, это не самое лучшее решение. <br>Можно воспользоваться стандартными функциями сравнения.
<source lang="Pascal">var
+
<source lang="Pascal">sr := min(a,b);
  p: integer;
+
if sr < c then
  a, b: integer;
+
   sr := min(max(a,b), c);</source>
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
+
*Даны координаты вершин треугольника и точка M. Принадлежит ли M треугольнику.
  b: real;
+
*Является ли 4-угольник ABCD корректно заданным.
  i: integer;
 
begin
 
  b := ReadReal;
 
  i := ReadInteger;
 
  MyInc(b, i);
 
  writeln(b);
 
end.</source>
 
Здесь
 
:b, i: фактические параметры при вызове MyInc
 
:a, n: формальные параметры (a — параметр-переменная; n — параметр-значение)
 
  
При передаче параметра-значения i в соответствующий формальный параметр копируется '''значение i'''.
+
<small>'''Лекция 4'''</small>
А вот механизм передачи параметра-переменной называется '''передачей по ссылке'''.
 
  
'''Ссылкой''' обычно называют другое имя некоторого объекта.
+
==Арифметические выражения==
 +
<h4>Основные сведения</h4>
 +
Каждое выражение имеет ''тип''. Выражение называется '''''арифметическим''''', если его тип — числовой.
 +
Выражение строится посредством ''операций''( унарных или бинарных ) и ''операндов''.
  
Для параметра-переменной имя соответствующего формального параметра выступает в роли ссылки переменной-фактического параметра.
+
В арифметических выражениях если <tt>a</tt> и <tt>b</tt> — одного типа, то и <tt>a '''op''' b</tt> принадлежит к тому же типу. ''Исключением'' является операция "'''/'''":
Все, что происходит со ''ссылкой'' внутри подпрограммы происходит и с ''самим объектом'' вне подпрограммы, поскольку это один и тот же объект.
+
:<tt>'''a / b'''</tt> — вещественное.
  
При передаче параметра ''по ссылке'' в подпрограмму передается не сама переменная, а её '''адрес''':
+
Если <tt>a</tt> и <tt>b</tt> принадлежат к различным тиапм, то выражение принадлежит к "'''старшему'''" типу.
  MyInc(var a: real; i: integer);
+
<br>Например:
          |
+
  byte < integer < int64
          '''@'''b ('''адрес''' b)
+
  integer < real
'''Замечание 1.''' В 32-битных ОС размер адреса — 4 байта, это позволяет адресовать примерно 4 Гб операционной памяти.
 
<br>'''Замечание 2.''' Размер адреса не зависит от размера переменной, поэтому при передаче параметров большого размера их предпочитают передавать по ссылке.
 
  
==Перегрузка имен подпрограмм==
+
В арифметические выражения могут входить стандартные функции:
Предположим, у нас описана процедура:
+
:<tt>exp(x)</tt>
<source lang="Pascal">procedure Swap(a, b: integer);
+
:<tt>ln(x)</tt>
begin
+
:<tt>abs(x)</tt> // модуль x
  var t := a;
+
:<tt>sin(x)</tt>
  a := b;
+
:<tt>cos(x)</tt>
  b := t;
+
:<tt>sqr(x)</tt> // квадрат x
end;</source>
+
:<tt>sqrt(x)</tt> // корень из x
Возможно ли описать процедуру с таким же именем, параметры которой были бы вещественными? Да, возможно:
+
:<tt>min(x,y)</tt>
<source lang="Pascal">procedure Swap(a, b: integer);
+
:<tt>max(x,y)</tt>
begin
+
:<tt>pow(x,y)</tt> // x в степени y
  var t := a;
 
  a := b;
 
  b := t;
 
end;
 
  
procedure Swap(a, b: real);
+
<h3>Порядок выполнения операций в арифметических выражениях</h3>
begin
+
*Операции с большим приоритетом выполняются первыми
  var t := a;
+
*Функции вычисляются до операций
  a := b;
+
*Выражение в скобках вычисляется раньше
  b := t;
+
*Операции с одинаковым приоритетом выполняются слева направо, если идут подряд.
end;</source>
 
Таким образом, в одном пространстве имен можно описать несколько подпрограмм с одним именем, но разными типами или количеством формальных параметров.
 
<br>При вызове такой подпрограммы на ''этапе компиляции'' типы фактических параметров сравниваются с типами формальных для всех версий этой подпрограммы и выбирается наиболее подходящая.
 
  
'''Замечание 1.''' Тип возвращаемого значения функции не участвует в операции разрешения перегрузки.
+
<h3>Операции div и mod для целых</h3>
 +
<tt>x '''div''' y = x / y, округленное до ближайшего целого по направлению к нулю.</tt> Это '''''результат''''' от ''целочисленного деления''.
 +
<br><tt>x '''mod''' y = x - (x div y) * y.</tt> Это '''''остаток''''' от ''целочисленного деления''.
  
Версии подпрограммы с одним именем называются '''перегруженными''', а определение того, какую версию выбрать — '''разрешением перегрузки'''.
+
'''Пример использования'''
 +
<br>Целочисленные операции часто применяются для определения '''четности''' числа:
 +
x '''mod''' 2 = 0  <->  x четное
 +
x '''mod''' 2 <> 0  <->  x — нечетное
  
'''Замечание 2.''' Разрешить перегрузку можно не всегда.
+
==Логические выражения==
<br>'''Пример.'''
+
<h3>Основные сведения</h3>
<source lang="Pascal">procedure p(i: integer; r: real);
+
Выражение назывется '''''логическим''''', если оно имеет тип <tt>'''boolean'''.</tt>
...
+
<br><tt>'''Пример'''.</tt>
 +
x < 0
 +
a >= b
 +
a <> 3
 +
Это простые логические выражения. Однако, с помщью '''логических операций''' можно составлять сложные.
 +
( бинарные )  ( унарные )
 +
  a '''and''' b        '''not''' a
 +
  a '''or''' b
 +
  a '''xor''' b
  
procedure p(r: real; c: char);
+
<h3>Таблицы истинности логических операций</h3>
...
+
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    |    F
 +
F | F |    F    |  F    |    T
 +
'''
 +
<h3>Сокращение вычислений логических выражений</h3>
 +
Большинство современных компиляторов, в т.ч. PascalABC.NET производит '''''сокращенное вычисление логических выражений'''''.
 +
<br>Это означает, что в выражении
 +
a '''and''' b
 +
если a — ложно, то b не вычисляется, а в
 +
a '''or''' b
 +
если a — истинно, b не вычисляется.
  
procedure p(r: real; i: integer);
+
Это очень полезно при вычислении таких выражений, как, например,
...
+
( y <> 0 ) '''and''' (x / y > 0 )
 +
Логически здесь все верно, однако, если бы не использовалось сокращенное вычисление, в случае равенства нулю y возникала бы ошибка деления на ноль.
  
begin
+
<h3>Логические переменные</h3>
  p(1, 2); //неоднозначность
+
Можно описывать логические переменные ( тип <tt>boolean</tt> ). Им можно присваивать логические выражения.
end.</source>
+
<br>Эти переменные принимают одно из двух возможных значений:
'''Замечание 3.''' В процессе разрешения перегрузки происходит ''неявное преобразование типов''.
+
:<tt>'''true'''</tt> ( истина )
<small>'''Лекция 11'''</small>
+
:<tt>'''false'''</tt> ( ложь )
  
==Параметры по умолчанию==
+
'''Пример''' использования логических переменных
<source lang="Pascal">procedure DoOp(a, b: integer; var res: integer; op: char := '+');
+
:Дано: прямоугольник со сторонами, параллельными осям координат, задан координатами абсцисс вертикальных сторон ( x1, x2 ) и ординатами горизонтальных ( y1, y2 ); точка M( x, y );
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">
==Предварительное объявление подпрограмм==
+
var inside, outside, bound: boolean;
<source lang="Pascal">procedure p;
 
 
begin
 
begin
   q;
+
   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>
  
procedure q;
+
==Побитовые операции==
begin
 
  ...
 
end;</source>
 
Такое описание вызовет ошибку компиляции, т.к. в '''<tt>p</tt>''' используется еще не известная процедура. Чтобы таких ошибок не возникало, нужно использовать ''предварительное объявление подпрограммы'':
 
<source lang="Pascal">procedure q; forward;
 
  
procedure p;
+
<h3>Побитовые операции and, or, xor</h3>
begin
+
'''Замечание.''' Работают только с целыми.
  q;
 
end;
 
  
procedure q;
+
Смысл такой — каждое целое переводится в '''двоичную''' систему счисления и производится ''побитовое'' применение этих операций.
  ...</source>
+
<br>'''Пример'''
'''forward-объявление''' делается для подпрограмм, которые ''обязательно'' будут описаны ниже. Если программа не будет описана в этом же файле, то, в конце компиляции этого файла, мы получим ошибку компилятора о <tt>'''forward'''</tt>, который был объявлен, но не описан.
+
5 '''and''' 10
 +
5<sub>10</sub> = 101<sub>2</sub>
 +
<br>7<sub>10</sub> = 111<sub>2</sub>
  
==Процедурные переменные==
+
101
===Процедурный тип и переменные===
+
    ( '''and''' )
<source lang="Pascal">var pr: procedure(a, b: real; var res: real);</source>
+
111
Переменная называется '''процедурной''' если ей можно присваивать процедуры или функции указанного типа.
+
———
<source lang="Pascal">
+
101<sub>2</sub> = 5<sub>10</sub>
type
 
  BinOpType = procedure(a, b: real; var res: real);
 
  
var
+
<h3>Операции shl и shr</h3>
  pr: BinOpType;
+
''Побитовый'' '''сдвиг влево''' и '''сдвиг вправо''' соответственно.
 +
<h4>shl</h4>
 +
x '''shl''' n = x * 2<sup>n</sup>
 +
Сдвигает двоичное представление x на n позиций влево.
 +
<h4>shr</h4>
 +
x '''shr''' n = x div 2<sup>n</sup>
 +
Сдвигает двоичное представление x на n позиций вправо.
 +
<h4>Примеры</h4>
 +
x = 5<sub>10</sub> = 101<sub>2</sub>
 +
 +
x shl 2 = <—(<sup>2</sup>)101
 +
              10100<sub>2</sub> = 20<sub>10</sub>
 +
 +
x shr 2 = 101—>(<sup>2</sup>)
 +
          001<sub>2</sub> = 1<sub>10</sub>
  
procedure AddOp(a, b: real; var res: real);
+
==Таблица приоритетов операций языка Object Pascal==
begin
+
# унарные <tt>'''+ - not'''</tt>
  res := a + b;
+
#имеющие смысл ''умножения'' <tt>'''* / div mod and shl shr'''</tt>
end;
+
#имеющие смысл ''сложения'' <tt>'''+ - or xor'''</tt>
procedure MultOp(a, b: real; var res: real);
+
#операции ''отношения'' <tt>'''<> <= >= < > in'''</tt>
begin
 
  res := a * b;
 
end;
 
  
begin
+
==Оператор case выбора варианта==
  pr := AddOp;
+
<h3>Синтакстис</h3>
  var r: real;
+
'''case''' <переключатель> '''of'''
  pr(1,2, r);  //вызов процедуры через процедурную переменную
+
  {<список выбора>: <оператор>;}
 +
  ['''else''' <оператор>[;]]
 +
'''end'''
  
   pr := MultOp;
+
<h3>Семантика</h3>
   pr(3,4, r);
+
Вначале вычисляется выражение-'''''переключатель''''', после чего его значение ищется в одном из <списков выбора>.
end.</source>
+
<br>Если значение попадает в какой-то <список выбора>, то выполняется соответствующий ему оператор, иначе, если есть ветвь <tt>'''else'''</tt>, то выполняется оператор по ветке <tt>'''else'''</tt>.
После присваивания процедурной переменной имени процедуры, эта процедура может быть вызвана через переменную:
+
<h3>Ограничения</h3>
<имя процедурной переменной>( <список параметров> );
+
*выражение-переключатель должно иметь так называемый '''порядковый''' тип:
Такой способ вызова процедуры является чрезвычайно гибким, поскольку позволяет менять вызываемое действие в процессе работы программы.
+
:''целый''
 +
:''символьный''
 +
:''перечислимый''
 +
НО НЕ строковый или вещественный.
 +
*значения в <списках выбора> не должны пересекаться.
 +
<h3>Примеры использования оператора выбора</h3>
 +
'''Пример 1.''' День недели
 +
<source lang="Pascal">
 +
case DayOfWeek of
 +
   1..5: writeln('Будний');
 +
  6,7: writeln('Выходный');
 +
   else writeln('Ошибка');
 +
end;</source>
 +
'''Пример 2.''' Цифра или буква
 +
<source lang="Pascal">
 +
var c: char;
 +
read(c);
 +
case c of
 +
  '0'..'9': writeln('Цифра');
 +
  'A'..'Z', 'a'..'z', 'а'..'я', 'А'..'Я', 'ё', 'Ё': writeln('Буква');
 +
end;</source>
  
'''Замечание.''' До первого присваивания процедурная переменная имеет тип <tt>'''nil'''</tt>. В этот момент вызывать процедуру через переменную нельзя — будет ошибка выполнения.
+
<small>'''Лекция 5'''</small>
  
===Функции обратного вызова (callback)===
+
==Циклы с предусловием (while) и постусловием (repeat)==
<source lang="Pascal">type ActionType = procedure;
+
<h3>Синтаксис цикла while</h3>
 +
'''while''' <условие> '''do'''  <— '''''заголовок цикла'''''
 +
  <оператор>  <— '''''тело цикла'''''
 +
 +
<условие>::= <логическое выражение>
 +
<h3>Семантика цикла while</h3>
 +
[[Изображение:Цикл_while_м.png]]
 +
<h3>Синтаксис цикла repeat</h3>
 +
'''repeat'''
 +
  <операторы>
 +
'''until''' <условие>
 +
<h3>Семантика цикла repeat</h3>
 +
[[Изображение:Цикл_repeat_м.png]]
 +
<h3>Зацикливание</h3>
 +
'''''Зацикливание''''' происходит, если:
 +
*условие цикла с '''предусловием''' всегда ''истинно''
 +
Пример
 +
<source lang="Pascal">while true do
 +
  <оператор></source>
 +
*условие цикла с '''постусловием''' всегда ''ложно''
 +
Пример
 +
<source lang="Pascal">repeat
 +
  <операторы>
 +
until false</source>
 +
'''''Итерация''''' — однократное повторение тела цикла
 +
<h4>Отличия между циклами while и repeat</h4>
 +
'''while'''
 +
:тело может не выполниться ни разу
 +
'''repeat'''
 +
:тело выполнится хотя бы один раз
 +
<h4>Примеры</h4>
 +
'''Пример 1.''' Сумма нечетных двузначных чисел
  
procedure DoActions(Action1, Action2, Action3: ActionType);
+
''С использованием while''
 +
<source lang="Pascal">s := 0; x := 11;
 +
while x < 100 do
 
begin
 
begin
   Action1;
+
   s += x;
   Action2;
+
   x += 2;
   Action3;
+
end;</source>
end;
+
''С использованием repeat''
 +
<source lang="Pascal">s := 0; x := 11;
 +
repeat
 +
  s += x;
 +
   x += 2;
 +
until x = 99;</source>
  
procedure Eat;
+
'''Пример 2.''' Факториал
begin
 
  writeln('Студент ест');
 
end;
 
procedure Think;
 
begin
 
  writeln('Студент думает');
 
end;
 
procedure Sleep;
 
begin
 
  writeln('Студент спит');
 
end;
 
  
begin
+
''С использованием repeat''
  DoActions(Sleep, Eat, Sleep);   // Первый алгоритм подготовки к сессии
+
<source lang="Pascal">var n: integer;
  DoActions(Think, Eat, Sleep);   // Второй алгоритм подготовки к сессии
+
read(n);
  DoActions(Sleep, Sleep, Think); // Третий алгоритм подготовки к сессии
+
var x := n;
end.</source>
+
var p :=1;
Процедурные переменные могут являться параметрами других процедур.
 
При вызове этих процедур им на вход в качестве параметров передаются имена процедур, котоые будут вызваны позже внутри основной процедуры.
 
<br>Другими словами, мы передаем в процедуру действие, которое должно быть вызвано (будет вызвано) в определенный момент в этой процедуре.
 
  
Такой вызов называется '''обратным вызовом (callback)'''.
+
repeat
<br>''Примечание.'' Прямой вызов — это передача процедуры в качестве параметра.
+
  p *= x;
 +
  x -= 1;
 +
until x = 1;</source>
 +
''С использованием while''
 +
<source lang="Pascal">var n: integer;
 +
read(n);
 +
var x := n;
 +
var p :=1;
  
'''Пример.''' Вычисление определённого интеграла методом прямоугольников.
+
while x > 1 do
<source lang="Pascal">type func = function(x: real): real;
 
function Integral(a, b: real; N: integer; f: func);
 
 
begin
 
begin
   assert(a < b);
+
   p *= x;
  var h := (b - a) / N;
+
   x -= 1;
   result := 0;
 
  var x := a;
 
  for var i:=0 to N-1 do
 
  begin
 
    result += f(x);
 
    x += h;
 
  end;
 
  result *= h;
 
 
end;</source>
 
end;</source>
 +
<h3>Моделирование repeat с помощью while</h3>
 +
'''repeat'''            ''Op''
 +
  '' Op''      ——>    '''while''' '''not''' A '''do'''
 +
'''until''' A;          '''begin'''
 +
                      ''Op''
 +
                    '''end;'''
 +
<h3>Моделирование while с помощью repeat</h3>           
 +
'''while''' A '''do'''        '''if''' A '''then'''
 +
  ''Op''        ——>    '''repeat'''
 +
                      '' Op''
 +
                      '''until not''' A
  
===Вызов нескольких процедур через процедурную переменную===
+
==Оператор цикла с параметром (for)==
<source lang="Pascal">type ActionType = procedure;
+
<h3>Синтаксис</h3>
 +
<заголовок цикла>
 +
  <тело цикла>
  
procedure Eat;
+
<заголовок цикла> ::= '''for''' <переменная>:=<выражение1> <направление> <выражение2> '''do'''
 +
<тело цикла> ::= <оператор>
 +
<направление> ::= to | downto
 +
<h3>Семантика</h3>
 +
<source lang="Pascal">var b := <выражение1>;
 +
var e := <выражение2>;
 +
  <переменная> := b;
 +
 +
while <переменная> <> e do
 
begin
 
begin
   ...
+
   <оператор>
 +
  <получить следующее значение переменной>
 +
  <переменная> += 1; | <переменная> -= 1;
 
end;
 
end;
procedure Think;
+
</source>
 +
 
 +
<получить следующее значение переменной> ::= <переменная> '''+'''= 1; | <переменная> '''-'''= 1;
 +
'''<big>Ограничения:</big>'''
 +
*выражения 1 и 2 должны быть совместимы по присваиванию с переменной
 +
*переменная должна иметь порядковый тип ( такой же, как и в case — целый, символьный или перечислимый )
 +
*переменная цикла for не должна меняться внутри цикла for
 +
*переменная цикла for должна быть описана в той же п/п, где используется цикл
 +
<h3>Дополнительные возможности PascalABC.NET</h3>
 +
Возможно ''описание переменной цикла в его заголовке'':
 +
<source lang="Pascal">for [var] i: integer := 1 to 5 do
 +
  <оператор></source>
 +
Возможно ''автоопределение типа при описании'':
 +
<source lang="Pascal">for var i := 1 to 5 do
 +
  <оператор></source>
 +
Переменная цикла, описанная в заголовке цикла, определена только внутри цикла.
 +
'''Замечание!''' Значение переменной цикла после завершения цикла не определено. Именно поэтому рекомендуется описывать переменную цикла в заголовке цикла.
 +
 
 +
==Примеры использования циклов==
 +
<h3>Пример 1. Табулирование функции</h3>
 +
:Дана f(x) на [a; b], разбитая на N частей.
 +
:Выдать таблицу значений в точках разбиения.
 +
'''Решение'''
 +
<source lang="Pascal">var a, b: real;
 +
var N: integer;
 +
read(a, b, N);
 +
 
 +
assert(N <> 0);
 +
assert(b > a);
 +
var h := (b - a) / N;</source>
 +
''Дальнейшее решение с помощью'' '''for''':
 +
<source lang="Pascal">for var i:=0 to N do
 
begin
 
begin
   ...
+
   writelnFormat('{0,6:f2} {1,9:f4}', x, f(x));
end;
+
  x += h;
procedure Sleep;
+
end;</source>
 +
''Дальнейшее решение с помощью'' '''while''':
 +
<source lang="Pascal">var eps := h / 2;
 +
while x < (b + eps) do
 
begin
 
begin
   ...
+
   writelnFormat('{0,6:f2} {1,9:f4}', x, f(x));
end;
+
  x += h;
 +
end;</source>
 +
'''Замечание.''' Вещественные числа в памяти компьютера представлены приближенно. Ошибка, которая возникает при представлении вещественного числа в памяти, называется '''''ошибкой округления'''''.
 +
<br>Ошибка, которая возникает в результате вычислений с вещественными числами называется '''''вычислительной погрешностью'''''.
  
var pr: ActionType;
+
:'''Вывод.''' Вещественные числа нельзя сравнивать на равенство, можно только на ''больше/меньше''.
 +
<small>'''Лекция 6'''</small>
 +
===Рекуррентные соотношения===
 +
Говорят, что последовательность данных
 +
x<sub>1</sub>, x<sub>2</sub>, x<sub>3</sub>,..., x<sub>n</sub>
 +
является '''''рекуррентной''''', если
 +
x<sub>k + 1</sub> = f( x<sub>k</sub> ), k = 1, 2, 3...
 +
<h3>Пример 2. Вывод степеней двойки</h3>
 +
<source lang="Pascal">var x := 1;
 +
for var i:=1 to 10 do
 
begin
 
begin
   pr += Sleep;
+
   writeln(x);
  pr += Eat;
+
  x *= 2;
   pr += Think;
+
end;</source>
   pr;
+
<h3>Пример 3. Последовательность Фибоначчи</h3>
   pr -= Eat;
+
<math>\begin{cases} x_1 = 1, x_2 = 1 \\ x_{k+1} = x_k + x_{k-1}\end{cases}</math>
   pr;
+
<source lang="Pascal">var a := 1;
end.</source>
+
var b := 1;
'''Замечание 1.''' Такое возможно только в .NET;
+
write(a, ' ', b, ' ');
<br>'''Замечание 2.''' Подобный механизм не имеет смысла использовать для функций;
+
for var i := 3 to 20 do
 +
begin
 +
   c := a + b;
 +
   write(c, ' ');
 +
   a := b;
 +
   b := c;
 +
end;</source>
 +
<h3>Пример 4. Вычисление НОД (алгоритм Евклида)</h3>
 +
<math>\begin{cases} x_1 = a \\ x_2 = b \\ x_{k+1} = x_{k-1} mod  x_k\end{cases}</math>
  
<small>'''Лекция 12'''</small>
+
'''Решение'''
==Методы разработки подпрограмм==
+
<source lang="Pascal">var a, b: integer;
'''Пример.''' Вывести таблицу простых чисел <= 1000.
+
read(a, b);
===Метод разработки ''сверху вниз''===
+
assert((a > 0) and (b > 0));
Метод разработки '''сверху вниз''' начинается с записи основного алгоритма в предположении,
+
repeat
что все подпрограммы уже написаны, т.е. код пишется ''в крупных командах''.
+
  c := a mod b;
 +
  a := b;
 +
  b := c;
 +
until c = 0;
 +
writeln(a);</source>
  
'''Решение.'''
+
===Пример 5. Суммирование рядов (конечных и бесконечных)===
<source lang="Pascal">writeln(2);
+
*<math> \sum_{i=1}^n  \frac{a^i}{i!}</math>
var x := 3;
+
Найдем рекуррентную связь между '''a<sub>i</sub>''':
while x <= 1000 do
+
x<sub>1</sub> = a
 +
x<sub>i</sub> = x<sub>i-1</sub> * a / i, i = 2, 3..
 +
'''Решение'''
 +
<source lang="Pascal">read(a, n);
 +
x := a;
 +
s := x;
 +
for var i:=2 to n do
 
begin
 
begin
   if IsPrime(x) then
+
   x *= a / i;
    writeln(x);
+
   s += x;
   x += 2;
 
 
end;</source>
 
end;</source>
После этого реализуем функцию IsPrime;
+
*<math> \sum_{i=1}^\infty  (-1)^i\frac{a^i}{i}</math>
 +
Для вычисления суммы ''бесконечного ряда'' в простейшем случае используют следующий метод:
 +
:задается некоторый малый <tt>'''eps'''</tt> и сумма <math>\sum_{i=1}^\infty  x_i</math> вычисляется, пока <math>|x_i| <\ eps</math>
  
Этот метод используется, когда:
+
'''Решение'''
*основной алгоритм понят, и его можно сразу записать
+
<source lang="Pascal">assert((a > 0) and (a < 1));
*задача четко поставлена
+
i := 1;
*имеется ровно одна задача, которую необходимо решить
+
s := 0;
 +
y := -a;
 +
repeat
 +
  s += y / i;
 +
  i += 1;
 +
  y *= -a;
 +
until abs(y / i) < eps;</source>
  
'''Замечание.''' В коде главной программы имеются вызовы других подпрограмм, которые реализуются позже (возможно, другими разработчиками);
+
===Пример 6. Нахождение max в последовательности чисел===
<br>На период, пока они не написаны, вместо них могут использоваться "'''заглушки'''" (подпрограммы с тривиальными телами)
+
'''Решение'''
 +
<source lang="Pascal">max := - real.MaxValue;
 +
for var i:=1 to n do
 +
begin
 +
  read(x);
 +
  if x > max then
 +
    max := x;
 +
end;</source>
  
Код каждой разрабатываемой подпрограммы также может разрабатываться методом сверху вниз.
+
<h3>Пример 7. Разложение целого числа на простые множители</h3>
===Метод разработки ''снизу вверх''===
+
Будем делить x на p, начиная с ''p = 2''. Если делится нацело, то p — множитель, если не делится, то увеличиваем p на 1, пока ''x <> 1''.
Вначале составляется "нижняя" подпрограмма, она всесторонне тестируется, и, потом,  
 
пишется основная программа или подпрограмма.
 
  
Метод используется, когда:
+
'''Решение'''
*на момент начала решения задача нечетко поставлена
+
<source lang="Pascal">read(x);
*когда задача — большая
+
p := 2;
*нет ясно выраженного главного алгоритма, а есть множество взаимосвязанных задач (примером может служить программа, реализующая деятельность университета)
+
repeat
 
+
  if x mod p = 0 then
'''Замечание 1.''' Написание самых "нижних" подпрограмм позволяет лучше войти в предметную область;
+
  begin
<br>'''Замечание 2.''' С какого-то момента разработка хотя бы упрощенного главного алгоритма становится необходимой;
+
    write(p, ' ');
 +
    x := x div p;
 +
  end
 +
  else
 +
    p += 1;
 +
until x = 1;</source>
  
Если между главным алгоритмом и "нижними" подпрограммами имеется несколько уровней,
+
<small>'''Лекция 7'''</small>
то имеет смысл '''сочетать оба метода разработки'''.
+
===Операторы break и continue===
==Оператор goto==
+
'''break''' оператор ''досрочного завершения цикла''.
===Синтаксис===
+
[[Изображение:break_м.png|none]]
'''goto''' <метка>
+
'''continue''' — оператор ''досрочного завершения текущей итерации'' цикла.
...
+
[[Изображение:continue_м.png|none]]
<метка>: <оператор>
+
<h3>Пример 8. Поиск заданного значения среди введенных</h3>
'''Метка''' представляет собой идентификатор или целое положительное число.
+
'''Решение 1'''. С использованием оператора ''break''
<br>Метка должна описываться в '''''разделе описаний меток''''' в виде:
+
<source lang="Pascal">var exists: boolean := false;
'''label''' <метка1>, <метка2>...;
+
for var i:=1 to n do
=== Семантические ограничения ===
 
*метка должна использоваться в той же подпрограмме, в которой описана;
 
*нельзя, используя goto, перейти внутрь какой-нибудь конструкции (цикла или условного оператора);
 
<xh4>Пример плохого кода</xh4>
 
<source lang="Pascal">label 1, 2, 3;
 
 
begin
 
begin
  goto 1;
+
  read(x);
  <100 операторов>
+
   if x = k then
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
 
<source lang="Pascal">label fin;
 
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
 
   if a*a + b*b = c*c then
 
 
   begin
 
   begin
     flag := true;
+
     exists := true;
     aa := a;
+
     break;
    bb := b;
 
    cc := c;
 
    goto fin;
 
 
   end;
 
   end;
 
+
end;</source>
fin: if flag then
+
'''Решение 2'''
      writeln(aa, bb, cc);</source>
+
<source lang="Pascal">var i := 1;
== Вызов подпрограммы на этапе выполнения ==
+
var exists: boolean:= false;
С каждой выполняющейся программой связан блок памяти, выделяющийся ей в начале работы и называемый '''программным стеком'''.
+
repeat
<br>Программный стек используется для хранения значений всех переменных, констант, а также промежуточных значений в процессе выполнения.
+
  read(x);
<здесь будет рисунок>
+
  if x = k then
При в ходе в подпрограмму в процессе выполнения, на программном стеке для нее выделяется специальный участок памяти, называемый '''записью активации''' ('''ЗА''') этой подпрограммы.
+
    exists := true;
 
+
  i += 1;
Данный участок выделяется на вершине стека, после чего вершина стека <tt>'''top'''</tt> "поднимается на размер ЗА.
+
until (i > n) or exists;</source>
<br>После окончания работы подпрограммы ее ЗА снимается со стека и вызывающая программа выполняется дальше.
+
<h3>Пример 9. Обработка последовательности, завершающейся нулем</h3>
 
+
:Вводятся числа. Конец ввода ноль.
'''Пример.'''
+
:Найти сумму и произвведение положительных чисел.
<br>[[Изображение:Работа_стека.gif]]
+
'''Решение'''
=== Устройство ЗА подпрограммы ===
+
<source lang="Pascal">s := 0;
[[Изображение:ЗА.png]]<br>
+
p := 1;
Штриховкой отмечены поля, заполняемые до запуска п/п.
+
while True do
 
 
=== Алгоритм входа в подпрограмму ===
 
# Увеличить '''<tt>top</tt>''' стека на размер ЗА п/п, вычесленного на этапе компиляции: '''<tt>top += r<sub>ЗА</sub></tt>'''
 
# Вычислить '''адрес возврата''' ('''АВ''') и '''адрес конца ЗА''' вызывающей п/п
 
# Вычислить значения всех фактических параметров и подставить их на место формальных.
 
# Перейти в коде программы на начало выполнения п/п
 
=== Алгоритм выхода из подпрограммы ===
 
# Уменьшить<tt>'''top'''</tt> стека, переместив его на '''адрес конца ЗА''' вызывающей подпрограммы
 
# В коде программы перейти по '''АВ'''
 
<small>'''Лекция 13'''</small>
 
=== Пример ===
 
<здесь будет рисунок>
 
 
 
После запуска программы ОС записывает код этой программы в оперативную память и выделяет дополнительный участок памяти для данных, называемый '''программным стеком'''.
 
 
 
При перемещении <tt>top</tt> на программном стеке накапливается мусор, потом в этот мусор опять записывается ЗА.
 
<br />Коды <tt>top</tt> и <tt>current</tt> хранятся в специальных регистрах.
 
 
 
'''Вывод 1.''' Если подпрограмма — маленькая, то накладные расходы на её вызов могут существенно превысить время работы самой подпрограммы.
 
<br />'''Вывод 2.''' Если локальные переменные или формальные параметры, передаваемые по значению имеют большой размер, то ЗА соответствующей подпрограммы будет большой. В результате при нескольких вложенных вызовах таких подпрограмм может произойти '''переполнение программного стека''' ('''Stack overflow'''), т.к. программный стек не увеличивается в размерах, а память под него выделяется один раз.
 
:'''''Выход.''''' Большие параметры по этой причине рекомендуется передавать не по значению, а ''по ссылке'', при этом на стек копируется не содержимое параметра, а его адрес.
 
 
 
'''Замечание.''' Адреса глобальных переменных, используемых в подпрограммах вычисляются по некоторому алгоритму ''на этапе выполнения'', следовательно, обращение к глобальным переменным происходит дольше, чем к локальным.
 
 
 
==Модули==
 
=== Введение ===
 
'''Модуль''' это совокупность взаимосвязанных процедур, функций, типов, переменных и констант,
 
предназначенных для решения ряда однотипных задач, и помещенных в специальным образом оформленный файл.
 
 
 
Модули разбивают большой проект на относительно независимые части, при этом, каждая часть "живет своей жизнью": модуль, написанный для одного программного проекта может быть использован в другом программном проекте.
 
 
 
Различают модули в виде '''''исходных текстов''''' и '''''откомпилированные модули'''''.
 
<br>Откомпилированные модули уменьшают суммарное время компиляции и позволяют ''скрывать программный код от модификации''.
 
 
 
'''Пример.'''
 
:Модуль ''MyLib''
 
<source lang="Pascal">unit MyLib;
 
 
 
interface      // раздел интерфейса
 
const
 
  MyPi = 3.14;
 
var
 
  size: integer := 10;
 
 
 
function AddSquares(a, b: real): real;
 
procedure InvertPrint(a, b: integer);
 
 
 
implementation  // раздел реализации
 
var i: integer;
 
 
 
function MySqr(a: real): real;
 
 
begin
 
begin
   result := a * a;
+
   read(x);
end;
+
  if x = 0 then
 +
    break;
 +
  if x < 0 then
 +
    continue;  //фильтр
 +
  s += x;
 +
  p *= x;
 +
end;</source>
 +
===Пример 10. Вычисление значения многочлена. Схема Горнера===
 +
Необходимо вычислить
 +
<math>\ a_0x^n + a_1x^{n-1} + ... + a_{n-1}x + a_n</math>
 +
если
 +
:a<sub>0</sub>...a<sub>n</sub> известны
 +
:x дано
  
function AddSquares(a, b: real): real;
+
'''Решение1'''
 +
<source lang="Pascal">var p := 1.0;
 +
var s := 0.0;
 +
for var i:=0 to n do
 
begin
 
begin
   result := MySqr(a) + MySqr(b);
+
   read(a);
end;
+
  s += p * a;
 
+
   p *= x;
procedure InvertPrint(a, b: integer);
+
end;</source>
begin
+
Это решение использует <tt>2(n + 1)</tt> умножений.
   write(b, ' ', a);
 
end;
 
 
 
end.            // конец модуля</source>
 
 
 
:Программа, подключающая модуль ''MyLib''
 
<source lang="Pascal">uses MyLib;
 
  
var
+
Однако есть и другое решение — '''схема Горнера'''.
  n: integer;
+
Оно основана на том, что
 +
<br><math>\ a_0x^2 + a_1x + a_2 = ((a_0)x + a_1)x + a_2</math>
  
 +
'''Решение2'''
 +
<source lang="Pascal">read(a);
 +
var res: real := a;
 +
for var i:=1 to n do
 
begin
 
begin
   writeln(size);
+
   read(a);
   writeln(AddSquares(3, 4));
+
   res *= x;
  InvertPrint(3,4);
+
  res += a;
end.</source>
+
end;</source>
 +
Итого — всего <tt>n</tt> умножений.
 +
===Пример 11. Поиск нуля функции на отрезке===
 +
Требуется найти корень уравнения <tt>f( x ) = 0</tt>
 +
*на отрезке <tt>[a; b]</tt>
 +
*с заданной точностью eps
 +
*<tt>f(x)</tt> непрерывна на этом отрезке
 +
*имеет на нем ровно 1 корень, т.е. <tt>f(a ) * f( b ) < 0</tt>
  
 +
Эта задача решается '''методом половинного деления'''.
 +
<source lang="Pascal">assert(b > a);
 +
var fa := f(a);
 +
var fb := f(b);
 +
assert(fa * fb < 0);
  
Модуль в языке Object Pascal состоит из двух разделов:
+
while b - a > eps do
*раздел '''интерфейса'''
+
begib
*раздел '''реализации'''
+
  var x := (b + a) / 2;
 +
  var fx := f(x);
 +
  if f(x) * f(a) > 0 then
 +
  begin
 +
    a := x;
 +
    fa := fx;
 +
  end
 +
  else
 +
    b := x;
 +
end;</source>
 +
<small>'''Лекция 8'''</small>
  
В разделе '''интерфейса''' описываются все
+
==Вложенные циклы==
<br>''переменные, константы, типы, заголовки подпрограмм'',
+
<h3>Метод последовательной детализации</h3>
<br>которые можно будет использовать в других модулях, подключающих данный.
+
'''Задача.''' Вывести все простые числа <= n
 
+
<source lang="Pascal">writeln(2);
В разделе '''реализации''' содержится
+
x := 3;
<br>''реализация подпрограмм'',
+
while x <= n do
<br>заголовки которых приведены в разделе интерфейса, а также
 
<br>описание ''вспомогательных констант, переменных, типов и подпрограмм'',
 
<br>которые нужны для реализации подпрограмм из раздела интерфейса и не видны из других модулей.
 
 
 
Данный принцип получил название '''принципа сокрытия информации'''.
 
<br>Его ''преимущества'':
 
*уменьшение количества информации, необходимой для понимания работы модуля;
 
*сокрытие реализации от клиентов;
 
*возможность легкого изменения реализации в следующих версиях без изменения интерфейса, т.е. без затрагивания интерфейса клиентов;
 
*возможность скрыть программный код от клиентов, предоставив им лишь откомпилированную версию модуля;
 
 
 
<small>'''Лекция 14'''</small>
 
=== Синтаксис модуля ===
 
'''unit''' <имя модуля>;
 
 
'''interface'''
 
['''uses''' <список модулей>;]
 
<раздел описаний модуля: для п/п — только заголовки>
 
 
'''implementation'''
 
['''uses''' <список модулей>;]
 
<раздел описаний, где даны реализации п/п из раздела interface>
 
 
 
['''initialization'''
 
  <операторы>]                ''' begin'''
 
                        |        <операторы>
 
['''finalization'''                  '''end'''.
 
  <операторы>]
 
 
 
=== Семантические ограничения ===
 
*Модули, перечисленные в секции <tt>'''uses'''</tt> в разделах интерфейса и реализации не должны пересекаться;
 
*''циклическая зависимость'' между модулями в разделе интерфейса '''запрещена''':
 
'''unit''' A;        '''unit''' B;
 
'''interface'''      '''interface'''
 
'''uses''' B;        '''uses''' A;
 
:тем не менее, если A ссылается на B в разделе ''интерфейса'', а B ссылается на A в разделе ''реализации'', то это '''допустимо''':
 
'''unit''' A;        '''unit''' B;
 
'''interface'''      '''interface'''
 
'''uses''' B;        '''implementation'''
 
                '''uses''' A;
 
:потому что модули '''''компилируются в два этапа''''':
 
:*интерфейс
 
:*реализация
 
 
 
=== Разделы инициализации и финализации модуля ===
 
'''unit''' A;                      <u>основная программа</u>
 
...
 
'''initialization'''                '''uses''' A;
 
  <операторы<sub>1</sub>>               
 
'''finalization '''                '''begin'''
 
  <операторы<sub>2</sub>>                  <операторы<sub>3</sub>>
 
'''end'''.                          '''end'''.
 
 
 
Операторы в разделе '''<tt>initialization</tt>''' модуля выполняются ''раньше'' тела основной программы,
 
<br /> а операторы в разделе <tt>'''finalization'''</tt> выполняются''после'' основной программы.
 
 
 
Т.е. последовательность выполнения будет такой:
 
<операторы<sub>1</sub>>
 
<операторы<sub>3</sub>>
 
<операторы<sub>2</sub>>
 
'''Примечание.''' В разделе ''инициализации'' обычно инициализируются глобальные переменные из раздела интерфейса.
 
 
 
<u>Пример</u>.
 
<source lang="Pascal">unit A;
 
 
 
interface
 
var
 
  RandomNumber: integer;
 
 
 
implementation
 
...
 
initialization
 
  RandomNumber := Random(100);
 
end.
 
</source>
 
 
 
=== Схема компиляции программы с модулями ===
 
<Здесь будет рисунок>
 
 
 
1. Компилируется файл основной программы.
 
<br />2. Если в нем встречена секция <tt>'''uses'''</tt>, то компилируются модули из этой секции ''слева направо''.
 
<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;    // из основной программы
+
   Если число x — простое, то
  k := 6;    // из B
+
    writeln(x);
  m := 7;    // из B
+
   x += 2;
  A.n := 8;  // из A
+
end;</source>
  B.n := 9;  // из B
+
<h3>Метод окаймления</h3>
  A.m := 10; // из A
+
'''Задача.''' Вывести A<sup>k</sup>, A = 2..10
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>
 
Передавать массив по значению крайне неэффективно (т.к.происходит копирование всех элементов на стек). Его следует передавать по ссылке: для этого есть специальное ключевое слово <tt>const</tt>, которое также запрещает изменять данные внутри п/п.
 
<source lang="delphi">
 
 
 
  procedure zzz(const a: IArr);
 
  procedure sss(var a: IArr);
 
 
 
</source>
 
Динамический массив
 
можно передавать в виде <tt>array of integer</tt>, т.к. для него определена структурная эквивалентность типов.
 
<source lang="delphi">
 
  procedure ttt(a: array of integer);
 
</source>
 
Передавать массив по ссылке необходимо только если внутри п/п нужно изменить длину массива.
 
Иначе можно передавать по значению, т.к. при вызове п/п на программный стек кладется только адрес участка памяти, хранящего значения элементов массива, а значит все изменения формального массива-параметра внутри п/п меняют соответствующий ему фактический параметр.
 
Массивы как возвращаемые значения функции
 
 
 
Часто требуется использовать массив как возвращаемое значение функции, для этого удобно использовать динамический массив, который хранит свою длину.
 
 
 
<h3>Переменное число параметров подпрограмм</h3>
 
 
 
П/п можно создавать с переменным количеством параметров.
 
Для этого исп. ключевое слово params.
 
При этом такие параметры, перечисленные через запятую, на этапе выполнения упаковываются в динамический массив, и п/п внутри работает именно с ним.
 
Пар-р params может быть только один и должен находиться последним в списке пар-ов.
 
 
 
<small>'''Лекция 18'''</small>
 
==Шаблоны подпрограмм==
 
 
 
Часто требуется выполнять одно и то же действие для разных типов данных.
 
В этом случае неудобно использовать перегрузку, т.к. п/п отличаются только заголовками.
 
 
 
Выход - шаблоны.
 
 
 
Пример:
 
 
 
  procedure Print<T> (array of T);
 
 
 
В момент вызова п/п происходит:
 
 
 
* выведение типов шаблона по фактическим параметрам
 
* инстанцирование - подстановка конкретных типов шаблонов и генерация кода подпрограммы с этими конкретными типами.
 
 
 
Если для такого типа код уже инстанцировался, то повторно действие не выполняется.
 
 
 
==Задачи на одномерные массивы==
 
 
 
* инвертирование массива (шаблон)
 
* поиск элемента в массиве - найти и вернуть индекс (шаблон)
 
 
 
можно с for и break,
 
 
 
можно и другим способом - с while
 
 
 
* поиск с барьером (шаблон). Его преимущества
 
* минимальный элемент массива и его индекс
 
* слияние двух упорядоченных массивов в один упорядоченный (барьер)
 
* сдвиг элементов массива влево (шаблон)
 
 
 
знакомство со значением 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>
 
 
 
Поля записей
 
 
 
Инициализация записей при описании (на примере Student)
 
 
 
Доступ к полям записей. Точечная нотация.
 
 
 
'''Замечание.''' Для записей имеет место именная эквивалентность типов.
 
 
 
=== Передача записей в подпрограммы. Записи как возвращаемые значения функции ===
 
 
 
Передача записей в п/п (Print(s)). Запись как возвращаемое значение функции (s := ReadStudent;).
 
 
 
Передавать записи по значению неэффективно, т.к. копируются все поля. Поэтому следуют передавать их по ссылке  (с ключевым словом const - если значения полей не изменяются, и var - если внутри п/п их требуется изменить.
 
 
 
Оператор with.
 
 
 
<source lang="delphi">
 
with <переменная типа запись> do
 
begin
 
  <поле>:=<значение>
 
end;
 
</source>
 
 
 
Оператор удобен, когда нужно выполнить достаточное количество операций с одной и той же переменной (имя этой переменной и точка добавляются неявно перед всеми полями)
 
 
 
=== Методы в записях ===
 
 
 
На примере Student.Print.
 
 
 
Запись как пространство имен
 
 
 
Запись выступает для своих полей и методов в качестве модуля (поскольку является набором данных и связанных с ними процедур и функций, оформленных как единое целое).
 
 
 
Методы работают в некотором окружении, в роли которого выступают поля записи и другие методы.
 
 
 
Т.о. возникает понятие ''инкапсуляции'':
 
:'''Инкапсуляция''' — хранение в записи одновременно данных и методов (запись представляет собой «капсулу»). <br />Инкапсуляция тесно связана с защитой данных: лишь некоторые члены в этой «капсуле» являются открытыми.
 
 
 
Создается окружение при описании переменной типа запись.
 
 
 
* Сравнение s.Print и Print(s)
 
 
 
'''s.Print''' - объектно-ориентированный подход, доминирует объект, который сам выполняет над собой действие.
 
 
 
'''Print(s)''' - процедурно-ориентированный подход, главным является действие, выполняющееся над переменной.
 
  
* Метод Init - инициализатор полей записи.
+
'''Метод окаймления''' заключается в том, что что мы окаймляем данный алгоритм внешним циклом, "''размораживая''" некоторый параметр.
  
<small>'''Лекция 23'''</small>
+
Итак, пусть A — фиксировано( "''заморожено''" ).
 
+
<source lang="Pascal">var p := 1.0;
==Создание простейших графических объектов==
+
for var i:=1 to k do
 
+
  p *= A;
<source lang="delphi">
+
write(p);</source>
RPoint = record x, y: integer end;
+
Теперь ''размораживаем'' A:
RRectangle = record p1,p2: RPoint end;
+
<source lang="Pascal">for A:=2 to 10 do
</source>
 
 
 
Для каждого типа определяются методы Print, Draw, MoveOn
 
 
 
<H3>Записи как средство упаковки параметров</H3>
 
 
 
'''Пример.'''
 
Используем определенные типы записей для создания перегруженных версий процедур Rectangle и Line:
 
 
 
<source lang="delphi">
 
Rectangle(x1,y1, x2,y2: integer)
 
Rectangle(p1,p2: RPoint)
 
Rectangle(r: RRectangle)
 
 
 
Line(x1,y1, x2,y2: integer)
 
Line(p1,p2: RPoint)
 
Line(r: RRectangle)
 
</source>
 
 
 
<H3>Переменная типа запись как объект</H3>
 
 
 
В модуле можно выносить реализацию методов записи в раздел реализации. При этом в разделе интерфейса внутри определения типа запись описываются только заголовки методов, а в разделе реализации перед именем метода необходимо писать <Имя записи>.
 
 
 
Поля записи как состояние объекта.
 
 
 
Методы записи как действия, воздействующие на состояние объекта.
 
 
 
Методы делятся на две категории
 
* меняющие состояние объекта (MoveOn для RPoint)
 
* осуществляющие доступ к состоянию объекта на чтение (Draw для RPoint)
 
 
 
Сортировка массива записей.
 
 
 
<small>'''Лекция 24'''</small>
 
==Индексная сортировка==
 
 
 
Часто физически переставлять элементы массива или вообще невозможно, или долго (в силу достаточно большого размера, например).
 
В этом случае удобно использовать ''перестановку индексов'' вместо перестановки самих элементов.
 
 
 
Идея заключается в том, чтобы описать '''индексный массив''' 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
 
begin
   for var i:=1 to n do
+
   ...
    Index[i] := i;
+
end;</source>
  for var i:=n downto 2 do
+
<h3>Переборные задачи</h3>
  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
+
:Дано равенство: a<sup>2</sup> + b<sup>2</sup> = c<sup>2</sup>, a,b,c — целые
  for var i:=1 to n do
+
:Вывести все такие тройки (a, b, c), что: a<=100, b<=1000, c<=1000;
    writeln(a[Index[i]].name,' ',a[Index[i]].course,' ',a[Index[i]].group);
+
'''Решение'''
end;
+
<source lang="Pascal">for var a:=1 to 1000 do
</source>
+
  for var b:=1 to 1000 do
 
+
    for var c:=1 to 1000 do
==Множества==
+
      if a*a + b*b = c*c then
 
+
         writeln(a, b, c);</source>
Описание множеств. Множества-константы. Пустое множество.
+
Однако, ясно, что
 
+
a<sup>2</sup> + b<sup>2</sup> = c<sup>2</sup> <=> b<sup>2</sup> + a<sup>2</sup> = c<sup>2</sup>
Операции над множествами:
+
'''Оптимизация'''
s1+s2 - объединение
+
<source lang="Pascal">for var a:=1 to 1000 do
s1*s2 - пересечение
+
  for var b:=1 to a-1 do
s1-s2 - разность
+
  begin
s += s1
+
    var c := round(sqrt(a*a + b*b));
s -= s1
+
    if a*a + b*b = c*c then
s *= s1
+
    begin
s1<s2 - строгое вложение
+
      writeln(a, b, c);
s1<=s2 - нестрогое вложене
+
      writeln(b, a, c);
s1>s2 - строгое вложение
+
    end;
s1<=s2 - нестрогое вложене
+
  end;</source>
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;
 
</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| Первая часть конспекта]]
 
*[[Основы программирования — Осенний семестр; Михалкович С.С.; 2008; I| Первая часть конспекта]]
 
*[[Конспекты|Другие конспекты]]
 
*[[Конспекты|Другие конспекты]]

Версия 21:11, 31 мая 2009

Содержание

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

Синтаксис

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

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

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

Семанитика

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

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

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

Операторы присваивания += и *=

Пример.

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

Лекция 3

Примеры использования :=

Пример 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 за минимальное число умножений.
Об этом читай тему.

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

Синтаксис

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

Семантика

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

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

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

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

Синтаксис

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

Семантика

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

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

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

Синтаксис

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

Семантика

Выражения в списке вычисляются и тих значения выводятся на экран. В случае writeln после выводао существляется переход на новую строку.

Форматы вывода

После каждого выражения в списке вывода можно использовать формат вывода в виде :a, где a — выражение целого типа. После вещественного типа — :a:b

a задает ширину поля вывода ( выравнивание по правому краю ).
b — количество знаков в дробной части.

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

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

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

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

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

a * b = a * b

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

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

Синтаксис

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

Семантика

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 попарно не равны) Эта задача имеет несколько вариантов решения.

if a < b then
  if a < c then
    if b < c then
      sr := b
    else
      sr := c
  else
    sr := a
else
  if a > c then
    if b > c then
      sr := b
    else
      sr := c
  else sr := a;

Очевидно, это не самое лучшее решение.
Можно воспользоваться стандартными функциями сравнения.

sr := min(a,b);
if sr < c then
  sr := min(max(a,b), c);

Самостоятельно.

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

Лекция 4

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

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

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

В арифметических выражениях если 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
min(x,y)
max(x,y)
pow(x,y) // x в степени y

Порядок выполнения операций в арифметических выражениях

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

Операции div и mod для целых

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

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

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

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

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

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

x < 0
a >= b
a <> 3

Это простые логические выражения. Однако, с помщью логических операций можно составлять сложные.

( бинарные )   ( унарные )
  a and b         not a
  a or b
  a xor b

Таблицы истинности логических операций

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    |    F
F | F |    F    |   F    |    T

Сокращение вычислений логических выражений

Большинство современных компиляторов, в т.ч. PascalABC.NET производит сокращенное вычисление логических выражений.
Это означает, что в выражении

a and b

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

a or b

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

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

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

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

Логические переменные

Можно описывать логические переменные ( тип 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.

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

Побитовые операции and, or, xor

Замечание. Работают только с целыми.

Смысл такой — каждое целое переводится в двоичную систему счисления и производится побитовое применение этих операций.
Пример

5 and 10

510 = 1012
710 = 1112

101
   ( and )
111
———
1012 = 510

Операции shl и shr

Побитовый сдвиг влево и сдвиг вправо соответственно.

shl

x shl n = x * 2n

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

shr

x shr n = x div 2n

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

Примеры

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

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

Синтакстис

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

Семантика

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

Ограничения

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

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

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

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

Пример 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;

Лекция 5

Циклы с предусловием (while) и постусловием (repeat)

Синтаксис цикла while

while <условие> do   <— заголовок цикла
  <оператор>   <— тело цикла

<условие>::= <логическое выражение>

Семантика цикла while

Цикл while м.png

Синтаксис цикла repeat

repeat
  <операторы>
until <условие>

Семантика цикла repeat

Цикл repeat м.png

Зацикливание

Зацикливание происходит, если:

  • условие цикла с предусловием всегда истинно

Пример

while true do
  <оператор>
  • условие цикла с постусловием всегда ложно

Пример

repeat
  <операторы>
until false

Итерация — однократное повторение тела цикла

Отличия между циклами while и repeat

while

тело может не выполниться ни разу

repeat

тело выполнится хотя бы один раз

Примеры

Пример 1. Сумма нечетных двузначных чисел

С использованием while

s := 0; x := 11;
while x < 100 do
begin
  s += x;
  x += 2;
end;

С использованием repeat

s := 0; x := 11;
repeat
  s += x;
  x += 2;
until x = 99;

Пример 2. Факториал

С использованием repeat

var n: integer;
read(n);
var x := n;
var p :=1;

repeat
  p *= x;
  x -= 1;
until x = 1;

С использованием while

var n: integer;
read(n);
var x := n;
var p :=1;

while x > 1 do
begin
  p *= x;
  x -= 1;
end;

Моделирование repeat с помощью while

repeat             Op
  Op       ——>     while not A do
until A;           begin
                     Op
                   end;

Моделирование while с помощью repeat

while A do         if A then
  Op         ——>     repeat
                       Op
                     until not A

Оператор цикла с параметром (for)

Синтаксис

<заголовок цикла>
  <тело цикла>
<заголовок цикла> ::= for <переменная>:=<выражение1> <направление> <выражение2> do
<тело цикла> ::= <оператор> 
<направление> ::= to | downto

Семантика

var b := <выражение1>;
var e := <выражение2>;
  <переменная> := b;
 
while <переменная> <> e do
begin
  <оператор>
  <получить следующее значение переменной>
  <переменная> += 1; | <переменная> -= 1;
end;
<получить следующее значение переменной> ::= <переменная> += 1; | <переменная> -= 1;

Ограничения:

  • выражения 1 и 2 должны быть совместимы по присваиванию с переменной
  • переменная должна иметь порядковый тип ( такой же, как и в case — целый, символьный или перечислимый )
  • переменная цикла for не должна меняться внутри цикла for
  • переменная цикла for должна быть описана в той же п/п, где используется цикл

Дополнительные возможности PascalABC.NET

Возможно описание переменной цикла в его заголовке:

for [var] i: integer := 1 to 5 do
  <оператор>

Возможно автоопределение типа при описании:

for var i := 1 to 5 do
  <оператор>

Переменная цикла, описанная в заголовке цикла, определена только внутри цикла. Замечание! Значение переменной цикла после завершения цикла не определено. Именно поэтому рекомендуется описывать переменную цикла в заголовке цикла.

Примеры использования циклов

Пример 1. Табулирование функции

Дана f(x) на [a; b], разбитая на N частей.
Выдать таблицу значений в точках разбиения.

Решение

var a, b: real;
var N: integer;
read(a, b, N);

assert(N <> 0);
assert(b > a);
var h := (b - a) / N;

Дальнейшее решение с помощью for:

for var i:=0 to N do
begin
  writelnFormat('{0,6:f2} {1,9:f4}', x, f(x));
  x += h;
end;

Дальнейшее решение с помощью while:

var eps := h / 2;
while x < (b + eps) do
begin
  writelnFormat('{0,6:f2} {1,9:f4}', x, f(x));
  x += h;
end;

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

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

Лекция 6

Рекуррентные соотношения

Говорят, что последовательность данных

x1, x2, x3,..., xn

является рекуррентной, если

xk + 1 = f( xk ), k = 1, 2, 3...

Пример 2. Вывод степеней двойки

var x := 1;
for var i:=1 to 10 do
begin
  writeln(x);
  x *= 2;
end;

Пример 3. Последовательность Фибоначчи

<math>\begin{cases} x_1 = 1, x_2 = 1 \\ x_{k+1} = x_k + x_{k-1}\end{cases}</math>

var a := 1;
var b := 1;
write(a, ' ', b, ' ');
for var i := 3 to 20 do
begin
  c := a + b;
  write(c, ' ');
  a := b;
  b := c;
end;

Пример 4. Вычисление НОД (алгоритм Евклида)

<math>\begin{cases} x_1 = a \\ x_2 = b \\ x_{k+1} = x_{k-1} mod x_k\end{cases}</math>

Решение

var a, b: integer;
read(a, b);
assert((a > 0) and (b > 0));
repeat
  c := a mod b;
  a := b;
  b := c;
until c = 0;
writeln(a);

Пример 5. Суммирование рядов (конечных и бесконечных)

  • <math> \sum_{i=1}^n \frac{a^i}{i!}</math>

Найдем рекуррентную связь между ai:

x1 = a
xi = xi-1 * a / i, i = 2, 3..

Решение

read(a, n);
x := a;
s := x;
for var i:=2 to n do
begin
  x *= a / i;
  s += x;
end;
  • <math> \sum_{i=1}^\infty (-1)^i\frac{a^i}{i}</math>

Для вычисления суммы бесконечного ряда в простейшем случае используют следующий метод:

задается некоторый малый eps и сумма <math>\sum_{i=1}^\infty x_i</math> вычисляется, пока <math>|x_i| <\ eps</math>

Решение

assert((a > 0) and (a < 1));
i := 1;
s := 0;
y := -a;
repeat
  s += y / i;
  i += 1;
  y *= -a;
until abs(y / i) < eps;

Пример 6. Нахождение max в последовательности чисел

Решение

max := - real.MaxValue;
for var i:=1 to n do
begin
  read(x);
  if x > max then
    max := x;
end;

Пример 7. Разложение целого числа на простые множители

Будем делить x на p, начиная с p = 2. Если делится нацело, то p — множитель, если не делится, то увеличиваем p на 1, пока x <> 1.

Решение

read(x);
p := 2;
repeat
  if x mod p = 0 then
  begin
    write(p, ' ');
    x := x div p;
  end
  else
    p += 1;
until x = 1;

Лекция 7

Операторы break и continue

break — оператор досрочного завершения цикла.

Break м.png

continue — оператор досрочного завершения текущей итерации цикла.

Continue м.png

Пример 8. Поиск заданного значения среди введенных

Решение 1. С использованием оператора break

var exists: boolean := false;
for var i:=1 to n do
begin
  read(x);
  if x = k then
  begin
    exists := true;
    break;
  end;
end;

Решение 2

var i := 1;
var exists: boolean:= false;
repeat
  read(x);
  if x = k then
    exists := true;
  i += 1;
until (i > n) or exists;

Пример 9. Обработка последовательности, завершающейся нулем

Вводятся числа. Конец ввода — ноль.
Найти сумму и произвведение положительных чисел.

Решение

s := 0;
p := 1;
while True do
begin
  read(x);
  if x = 0 then
    break; 
  if x < 0 then
    continue;  //фильтр
  s += x;
  p *= x;
end;

Пример 10. Вычисление значения многочлена. Схема Горнера

Необходимо вычислить <math>\ a_0x^n + a_1x^{n-1} + ... + a_{n-1}x + a_n</math> если

a0...an известны
x дано

Решение1

var p := 1.0;
var s := 0.0;
for var i:=0 to n do
begin
  read(a);
  s += p * a;
  p *= x;
end;

Это решение использует 2(n + 1) умножений.

Однако есть и другое решение — схема Горнера. Оно основана на том, что
<math>\ a_0x^2 + a_1x + a_2 = ((a_0)x + a_1)x + a_2</math>

Решение2

read(a);
var res: real := a;
for var i:=1 to n do
begin
  read(a);
  res *= x;
  res += a;
end;

Итого — всего n умножений.

Пример 11. Поиск нуля функции на отрезке

Требуется найти корень уравнения f( x ) = 0

  • на отрезке [a; b]
  • с заданной точностью eps
  • f(x) непрерывна на этом отрезке
  • имеет на нем ровно 1 корень, т.е. f(a ) * f( b ) < 0

Эта задача решается методом половинного деления.

assert(b > a);
var fa := f(a);
var fb := f(b);
assert(fa * fb < 0);

while b - a > eps do
begib
  var x := (b + a) / 2;
  var fx := f(x);
  if f(x) * f(a) > 0 then
  begin
    a := x;
    fa := fx;
  end
  else
    b := x;
end;

Лекция 8

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

Метод последовательной детализации

Задача. Вывести все простые числа <= n

writeln(2);
x := 3;
while x <= n do
begin
  Если число x — простое, то
    writeln(x);
  x += 2;
end;

Метод окаймления

Задача. Вывести Ak, A = 2..10

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

Итак, пусть A — фиксировано( "заморожено" ).

var p := 1.0;
for var i:=1 to k do
  p *= A;
write(p);

Теперь размораживаем A:

for A:=2 to 10 do
begin
  ...
end;

Переборные задачи

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

Задача

Дано равенство: a2 + b2 = c2, a,b,c — целые
Вывести все такие тройки (a, b, c), что: a<=100, b<=1000, c<=1000;

Решение

for var a:=1 to 1000 do
  for var b:=1 to 1000 do
    for var c:=1 to 1000 do
      if a*a + b*b = c*c then
        writeln(a, b, c);

Однако, ясно, что

a2 + b2 = c2 <=> b2 + a2 = c2

Оптимизация

for var a:=1 to 1000 do
  for var b:=1 to a-1 do
  begin
    var c := round(sqrt(a*a + b*b));
    if a*a + b*b = c*c then
    begin
      writeln(a, b, c);
      writeln(b, a, c);
    end;
  end;

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

Ссылки