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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Решение 1.

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

Решение 2.

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

Решение 3.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

a * b = a * b

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

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

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

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

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

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

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

byte < integer < int64
integer < real

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

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

exp(x)
ln(x)
abs(x)  // модуль x
sin(x)
cos(x)
sqr(x)  // квадрат x
sqrt(x) // корень из x 
round(x) - целое, полученное в результате округления вещественного x
trunc(x) - целое, полученное в результате отбрасывания дробной части у вещественного x
min(x,y) 
max(x,y) 
power(x,y)// x в степени y
random(n)// псевдослучайное целое число от 0 до n-1
random(a,b)// псевдослучайное целое число от a до b

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

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

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

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

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

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

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

x < 0
a >= b
a <> 3

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

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

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

a | b | a and b | a or b | a xor b 

T | T |    T    |   T    |    F
T | F |    F    |   T    |    T
F | T |    F    |   T    |    T
F | F |    F    |   F    |    F

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

a and b

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

a or b

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

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

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

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

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

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

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

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

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

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

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

5 and 7

510 = 1012
710 = 1112

101
   ( and )
111
———
1012 = 510

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

<xh4> shl </xh4>

x shl n = x * 2n

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

<xh4> shr </xh4>

x shr n = x div 2n

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

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

x = 510 = 1012

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Решение 1.

Достаточно рассмотреть случай, когда координаты точек равны 1,2 и 3

Рассмотрим все возможные ситуации распределения этих значений между a,b,c:

a  b  c
1  2  3  a<b
1  3  2  a<b
2  1  3
2  3  1  a<b
3  1  2
3  2  1

При a<b возможны три варианта, в двух из которых a<c. При a>b возможны три варианта, в двух из которых a>c.

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

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

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

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

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

Решение 2.

var m1 := min(a,b);
var m2 := max(a,b);
a  b  c  
1  2  3  c>m2
1  3  2  
2  1  3  c>m2
2  3  1  c<m1
3  1  2  
3  2  1  c<m1
var m1 := min(a,b);
var m2 := max(a,b);
if c<m1 then
  m := m1
else if c>m2 then
  m := m2
else m := c;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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