Пользовательские подпрограммы в Pascal: теоретические основы, практическая реализация и оптимизация в Free Pascal/Lazarus для курсовой работы

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

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

В рамках данной курсовой работы мы погрузимся в мир пользовательских подпрограмм в языке Pascal, исследуем их теоретические основы, синтаксические конструкции и семантические нюансы. Мы детально рассмотрим различия между процедурами и функциями, изучим тонкости механизмов передачи параметров, а также разберем, как область видимости переменных влияет на поведение кода. Особое внимание будет уделено практическим аспектам работы с подпрограммами в среде Free Pascal и Lazarus, раскрывая специфические оптимизации компилятора и лучшие практики проектирования, основанные на принципах модульности и единственной ответственности. Цель — предоставить студентам исчерпывающее руководство, которое не только углубит их понимание языка Pascal, но и подготовит к созданию качественного, эффективного и поддерживаемого программного обеспечения.

Основы подпрограмм в Pascal: определение и назначение

Что такое подпрограмма?

В основе любого сложного программного проекта лежит идея декомпозиции — разбиения целого на части. В языке Pascal эта идея реализована через концепцию подпрограммы. По своей сути, подпрограмма представляет собой именованную, логически законченную группу операторов, объединенную для выполнения конкретной задачи. Это как мини-программа внутри основной программы, которая может быть вызвана для выполнения из различных мест кода столько раз, сколько потребуется.

Каждая подпрограмма характеризуется следующими основными компонентами:

  • Имя (идентификатор): Уникальное имя, по которому она вызывается.
  • Список формальных параметров (необязательно): Набор переменных, через которые подпрограмма получает входные данные или возвращает результаты.
  • Тело подпрограммы: Последовательность операторов, заключенных между ключевыми словами begin и end;, которые определяют логику ее работы.
  • Раздел описаний (необязательно): Локальные константы, типы, переменные и даже другие подпрограммы, которые доступны только внутри данной подпрограммы.

Важно различать пользовательские подпрограммы от встроенных. Встроенные (или стандартные) подпрограммы, такие как Writeln или Readln, являются частью самого языка и доступны «из коробки». Пользовательские же подпрограммы создаются самим программистом для решения специфических задач конкретного проекта. Именно они дают программисту возможность строить модульные, легко управляемые системы.

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

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

  1. Улучшение читаемости и понимания кода: Вместо гигантского блока операторов, который трудно охватить целиком, программа состоит из вызовов именованных подпрограмм, чьи имена говорят об их назначении. Это значительно упрощает чтение и понимание логики программы.
  2. Избегание дублирования кода: Один из худших «запахов» в коде — это дублирование. Если одна и та же последовательность действий встречается в разных частях программы, это верный признак того, что её следует оформить как подпрограмму. Изменение логики в одном месте подпрограммы автоматически распространяется на все её вызовы, предотвращая ошибки и сокращая время на модификацию.
  3. Упрощение отладки: Если в программе обнаружена ошибка, легче локализовать её в небольшой, изолированной подпрограмме, чем искать в тысячах строк монолитного кода.
  4. Повторное использование кода (reusability): Разработанная и отлаженная подпрограмма может быть многократно использована в различных частях текущей программы или даже в совершенно других проектах, что существенно экономит время и ресурсы.
  5. Локализация изменений: Если требуется изменить реализацию конкретного алгоритмического действия, достаточно внести изменения только в тело соответствующей подпрограммы, не затрагивая остальную часть программы.

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

Общая структура описания подпрограмм в Pascal

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

Заголовок подпрограммы определяет её тип (процедура или функция), имя и список формальных параметров.

Тело подпрограммы, заключенное между begin и end;, содержит исполняемые операторы. Перед телом может располагаться раздел описаний, где объявляются локальные константы, типы, переменные и даже вложенные подпрограммы, доступные только внутри данной подпрограммы.

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

program ExampleProgram;

  // Раздел описания глобальных переменных
  var
    GlobalVar: Integer;

  // Раздел описания подпрограмм
  // Здесь будут располагаться объявления процедур и функций

  // Пример структуры процедуры
  procedure MyProcedure(Parameter1: Integer; var Parameter2: Real);
  var
    LocalVar: String; // Локальная переменная процедуры
  begin
    // Операторы процедуры
    LocalVar := 'Hello';
    Writeln('Внутри MyProcedure. Parameter1 = ', Parameter1);
    Parameter2 := Parameter2 * 2; // Изменение Parameter2
  end;

  // Пример структуры функции
  function CalculateSum(A, B: Integer): Integer;
  var
    TempSum: Integer; // Локальная переменная функции
  begin
    TempSum := A + B;
    Result := TempSum; // Присваивание значения результату функции
  end;

begin
  // Основная программа
  GlobalVar := 10;
  Writeln('До вызова MyProcedure. GlobalVar = ', GlobalVar);

  MyProcedure(5, GlobalVar); // Вызов процедуры, GlobalVar передается по ссылке
  Writeln('После вызова MyProcedure. GlobalVar = ', GlobalVar); // GlobalVar изменилась

  Writeln('Сумма 7 и 3 = ', CalculateSum(7, 3)); // Вызов функции

end.

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

Процедуры и функции: синтаксические и семантические различия

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

Процедуры: выполнение действий и побочные эффекты

Процедуры в Pascal предназначены для выполнения определенной последовательности действий или операций. Их основная задача — не вычислить и вернуть одно значение, а скорее изменить состояние программы или выполнить какую-либо операцию, которая может включать:

  • Вывод информации на экран или в файл.
  • Чтение данных с клавиатуры или из файла.
  • Изменение значений глобальных переменных (так называемые «побочные эффекты»).
  • Модификацию переданных по ссылке параметров.
  • Выполнение сложных алгоритмов, которые не имеют одного «результата» в привычном математическом смысле, но влияют на окружение.
  • Возврат нескольких результатов (через параметры, переданные по ссылке).

Синтаксис объявления процедуры:

procedure ИмяПроцедуры (список_формальных_параметров);
  // Раздел описаний (локальные константы, типы, переменные, вложенные подпрограммы)
begin
  // Операторы, выполняющие действия процедуры
end;

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

// Процедура для вывода приветствия
procedure SayHello;
begin
  Writeln('Привет, мир!');
end;

// Процедура для обмена значениями двух переменных
procedure Swap(var A, B: Integer);
var
  Temp: Integer;
begin
  Temp := A;
  A := B;
  B := Temp;
  Writeln('Значения внутри Swap: A = ', A, ', B = ', B);
end;

// Процедура для чтения массива с клавиатуры
procedure ReadArray(var Arr: array of Integer; Size: Integer);
var
  I: Integer;
begin
  Writeln('Введите ', Size, ' целых чисел:');
  for I := 0 to Size - 1 do
  begin
    Write('Элемент [', I, ']: ');
    Readln(Arr[I]);
  end;
end;

begin
  SayHello; // Вызов процедуры без параметров

  var X, Y: Integer;
  X := 5;
  Y := 10;
  Writeln('До Swap: X = ', X, ', Y = ', Y);
  Swap(X, Y); // Вызов процедуры с параметрами по ссылке
  Writeln('После Swap: X = ', X, ', Y = ', Y); // X и Y поменялись местами

  var MyArr: array[0..2] of Integer;
  ReadArray(MyArr, 3); // Вызов процедуры для чтения массива
end.

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

Функции: вычисление и возврат единственного значения

В отличие от процедур, функции в Pascal специально предназначены для вычисления и возврата одного значения определенного типа. Это значение может быть любого простого или ссылочного типа (Integer, Real, Boolean, String и т.д.). Функции часто используются для инкапсуляции математических вычислений, логических проверок или любых других операций, результатом которых является одно четко определенное значение.

Ключевые особенности функций:

  • Возврат значения: Функция всегда возвращает одно значение, тип которого должен быть указан в её заголовке.
  • Использование в выражениях: Результат функции может быть использован как операнд в выражениях, подобно константе или переменной.
  • Механизмы возврата значения: В теле функции для присваивания возвращаемого значения используется либо присваивание имени функции (ИмяФункции := Результат;), либо специальная переменная Result (рекомендуется для ясности и совместимости с Object Pascal).

Синтаксис объявления функции:

function ИмяФункции (список_формальных_параметров): ТипРезультата;
  // Раздел описаний
begin
  // Операторы, выполняющие вычисления
  ИмяФункции := ВычисленноеЗначение; // или Result := ВычисленноеЗначение;
end;

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

// Функция для вычисления квадрата числа
function Square(Number: Integer): Integer;
begin
  Result := Number * Number; // Использование переменной Result
end;

// Функция для определения, является ли число четным
function IsEven(Number: Integer): Boolean;
begin
  if (Number mod 2 = 0) then
    IsEven := True // Присваивание имени функции
  else
    IsEven := False;
end;

// Функция для нахождения максимального из двух чисел
function Max(A, B: Real): Real;
begin
  if A > B then
    Result := A
  else
    Result := B;
end;

begin
  var Num: Integer = 7;
  Writeln('Квадрат числа ', Num, ' = ', Square(Num)); // Использование функции в Writeln

  if IsEven(10) then
    Writeln('10 - четное число.')
  else
    Writeln('10 - нечетное число.');

  Writeln('Максимум из 15.5 и 8.2 = ', Max(15.5, 8.2)); // Использование функции как операнда
  var ResultSum: Real := Max(10.0, 20.0) + Square(5);
  Writeln('Результат сложения: ', ResultSum); // Использование нескольких функций в выражении
end.

Когда выбирать процедуру, а когда функцию?

Выбор между процедурой и функцией зависит от характера задачи, которую необходимо решить:

Критерий / Тип подпрограммы Процедура Функция
Основное назначение Выполнение последовательности действий, изменение состояния программы, ввод/вывод, побочные эффекты. Вычисление и возврат одного значения определенного типа.
Возвращаемое значение Не возвращает явного значения. Может возвращать несколько «результатов» через var-параметры. Всегда возвращает одно значение указанного в заголовке типа.
Использование Вызывается как отдельный оператор. Может использоваться как часть выражения или в качестве операнда.
Синтаксис заголовка procedure ИмяПроцедуры(параметры); function ИмяФункции(параметры): ТипРезультата;
Пример ситуации Инициализация переменных, вывод данных, сортировка массива, изменение структуры данных, сложный ввод/вывод. Расчет математических выражений, проверка условий (возврат Boolean), получение значения элемента, форматирование.

Обоснование выбора:

  • Используйте функцию, когда:
    • Вам нужен один, четко определенный результат, который будет использоваться в выражениях или для принятия решений.
    • Подпрограмма не должна (или должна минимально) изменять состояние программы за пределами своих параметров (идеально, если она «чистая» и не имеет побочных эффектов).
    • Примеры: CalculateTax(Amount), GetDistance(Point1, Point2), IsValidInput(InputString).
  • Используйте процедуру, когда:
    • Вам необходимо выполнить набор действий, которые изменяют состояние программы (например, вывести данные, изменить массив, прочитать информацию).
    • Подпрограмма не возвращает явного значения или возвращает несколько значений через var-параметры.
    • Примеры: PrintReport(Data), SortArray(MyArray), InitializeGame(), ProcessUserData(UserRecord).

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

Механизмы передачи параметров: эффективность и безопасность данных

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

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

Для начала разберемся с терминологией:

  • Формальные параметры: Это переменные, которые указываются в заголовке подпрограммы при её описании. Они служат «заполнителями» для данных, которые будут получены извне. Формальные параметры имеют свои имена и типы.
  • Фактические параметры: Это конкретные значения, переменные или выражения, которые передаются в подпрограмму при её вызове.

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

  1. Количеству: Число фактических параметров должно быть равно числу формальных.
  2. Порядку следования: Порядок фактических параметров должен совпадать с порядком формальных.
  3. Типу: Типы фактических параметров должны быть совместимы с типами соответствующих формальных параметров (точное совпадение или совместимость по присваиванию, в зависимости от механизма передачи).

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

Существуют три основных способа передачи параметров в Pascal: по значению, по ссылке (переменной) и по константе.

Передача параметров по значению (Value parameters)

Механизм: При передаче параметра по значению, компилятор создает копию значения фактического параметра и присваивает её соответствующему формальному параметру внутри подпрограммы.

Синтаксис: Формальный параметр описывается без каких-либо ключевых слов (var или const) перед его именем.

procedure ProcessValue(A: Integer); // A - параметр по значению
begin
  Writeln('Внутри ProcessValue (до изменения): A = ', A);
  A := A * 2; // Изменяем локальную копию A
  Writeln('Внутри ProcessValue (после изменения): A = ', A);
end;

var
  X: Integer = 10;
begin
  Writeln('До вызова ProcessValue: X = ', X);
  ProcessValue(X); // Передаем X по значению
  Writeln('После вызова ProcessValue: X = ', X); // X осталось неизменным
  ProcessValue(25); // Можно передавать константы
  ProcessValue(X + 5); // Можно передавать выражения
end.

Особенности:

  • Независимость: Изменения, вносимые в формальный параметр внутри подпрограммы, не влияют на исходную фактическую переменную. Это обеспечивает безопасность данных вызывающей программы.
  • Гибкость фактических параметров: В качестве фактического параметра можно указывать любое выражение, константу или переменную, чей тип совместим по присваиванию с типом формального параметра.
  • Эффективность: Для простых типов данных (Integer, Boolean, Char, Real) это достаточно эффективно. Однако для больших структур данных (массивы, записи, строки большой длины) копирование может занимать значительное время и потреблять много памяти, что снижает производительность.

Передача параметров по ссылке (Variable parameters)

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

Синтаксис: Формальный параметр описывается с ключевым словом var перед его именем.

procedure ProcessVar(var B: Integer); // B - параметр по ссылке
begin
  Writeln('Внутри ProcessVar (до изменения): B = ', B);
  B := B * 2; // Изменяем непосредственно фактическую переменную
  Writeln('Внутри ProcessVar (после изменения): B = ', B);
end;

var
  Y: Integer = 10;
begin
  Writeln('До вызова ProcessVar: Y = ', Y);
  ProcessVar(Y); // Передаем Y по ссылке
  Writeln('После вызова ProcessVar: Y = ', Y); // Y изменилось
  // ProcessVar(25); // Ошибка! Нельзя передавать константы или выражения по ссылке
end.

Особенности:

  • Прямое влияние: Изменения, вносимые в формальный параметр внутри подпрограммы, напрямую отражаются на фактической переменной в вызывающей программе.
  • Ограничение фактических параметров: В качестве фактического параметра может выступать только переменная. Передавать константы или выражения запрещено, так как они не имеют адреса в памяти, который можно было бы изменить.
  • Точное совпадение типов: Типы формального и фактического var-параметра должны в точности совпадать, а не просто быть совместимыми по присваиванию.
  • Роль: Используется для входно-выходных параметров (когда значение используется внутри подпрограммы и затем модифицируется) и для выходных параметров (когда подпрограмма инициализирует или присваивает значение переменной).
  • Эффективность: Для больших структур данных передача по ссылке гораздо эффективнее, так как копирование не происходит – передается только адрес.

Передача параметров по константе (Constant parameters)

Механизм: Передача параметров по константе представляет собой гибридный подход. С точки зрения безопасности данных, он работает как передача по значению — компилятор гарантирует, что значение формального параметра не будет изменено внутри подпрограммы. Однако с точки зрения эффективности, особенно для сложных и больших типов данных, Pascal (и Free Pascal в частности) может реализовать это как передачу по ссылке.

Синтаксис: Формальный параметр описывается с ключевым словом const перед его именем.

procedure ProcessConst(const C: Integer); // C - параметр по константе
begin
  Writeln('Внутри ProcessConst: C = ', C);
  // C := C * 2; // Ошибка компиляции: Нельзя присваивать значение константному параметру
end;

var
  Z: Integer = 15;
begin
  ProcessConst(Z);
  ProcessConst(30); // Можно передавать константы
  ProcessConst(Z + 10); // Можно передавать выражения
end.

Детализация Free Pascal:

  • Гарантия неизменяемости: Ключевое слово const является мощным средством для обеспечения безопасности данных. Компилятор Free Pascal активно следит за тем, чтобы никакие операции внутри подпрограммы не пытались изменить значение const-параметра, выдавая ошибку компиляции в противном случае.
  • Оптимизация производительности: Для больших типов данных (например, записи, объекты, длинные строки, массивы) Free Pascal оптимизирует передачу const-параметров, передавая их по ссылке. Это исключает дорогостоящее копирование всего содержимого, но при этом сохраняет семантику «только для чтения». Такая оптимизация достигается благодаря тому, что компилятор знает о невозможности изменения параметра и может избежать создания его полной копии в стеке. Это приводит к уменьшению размера стека и, как следствие, к повышению производительности.
  • Refcounted типы: Для типов со счетчиком ссылок (refcounted types), таких как string, AnsiString, WideString, динамические массивы и интерфейсы, const-параметры особенно важны. Использование const позволяет компилятору опустить неявные блоки try/finally, которые обычно генерируются для управления счетчиками ссылок при копировании. Это дополнительно оптимизирует выполнение подпрограмм, особенно если такие параметры передаются часто.
  • constref параметры (Free Pascal 2.5.1+): Начиная с версии 2.5.1, Free Pascal ввел constref параметры, которые явно указывают, что параметр передается по ссылке, но его значение является константой. Это полезно для больших структур, когда требуется максимальная прозрачность механизма передачи и явная декларация того, что параметр — это ссылка на константу. constref параметры используются реже, чем просто const, но могут быть полезны в специфических сценариях, где важен контроль над внутренним механизмом.

Сводная таблица механизмов передачи параметров:

Механизм Ключевое слово Копирование значения Изменение фактического параметра Что можно передать в качестве фактического параметра Требования к типу Основное назначение
По значению Отсутствует Да (всегда) Нет Переменная, константа, выражение Совместимость по присваиванию Входные данные, безопасность, для простых типов.
По ссылке var Нет (адрес) Да Только переменная Точное совпадение Входно-выходные/выходные данные, эффективность для больших типов.
По константе const Нет (оптимизация FPC) Нет (гарантировано компилятором) Переменная, константа, выражение Совместимость по присваиванию Входные данные, эффективность для больших типов, безопасность.

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

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

Эффективное программирование требует глубокого понимания того, где и когда переменные доступны, а также как долго они существуют. В Pascal эти аспекты регулируются понятиями области видимости (scope) и жизненного цикла переменных, которые особенно ярко проявляются в контексте подпрограмм и их вложенности.

Глобальные и локальные переменные

Все переменные в Pascal можно разделить на две основные категории в зависимости от места их объявления:

  1. Глобальные переменные: Объявляются в основной программе, вне каких-либо подпрограмм. Они доступны для использования во всей программе, начиная с места их объявления и до самого конца. Их жизненный цикл охватывает всё время выполнения программы.
  2. Локальные переменные: Объявляются внутри конкретной подпрограммы (процедуры или функции) в её собственном разделе var. Они видны и доступны только внутри той подпрограммы, где были объявлены.

Принцип «экранирования» (shadowing):
Если глобальная переменная и локальная переменная имеют одно и то же имя, то внутри подпрограммы, где объявлена локальная переменная, доступной будет только локальная переменная. Она «экранирует» глобальную, делая её временно недоступной по имени. Это помогает избежать случайных конфликтов имен, но может привести к путанице, если не использовать осмысленные имена.

program ScopeExample;

var
  Counter: Integer = 0; // Глобальная переменная

procedure IncrementCounter;
var
  Counter: Integer = 10; // Локальная переменная с тем же именем
begin
  Writeln('Внутри IncrementCounter (локальный Counter): ', Counter); // Выведет 10
  Counter := Counter + 1; // Изменяется локальный Counter
  Writeln('Внутри IncrementCounter (локальный Counter после изменения): ', Counter); // Выведет 11
end;

begin
  Writeln('До вызова IncrementCounter (глобальный Counter): ', Counter); // Выведет 0
  IncrementCounter;
  Writeln('После вызова IncrementCounter (глобальный Counter): ', Counter); // Выведет 0 (глобальный не изменился)
end.

В данном примере глобальная переменная Counter осталась неизменной, так как процедура IncrementCounter работала с собственной локальной переменной с тем же именем.

Жизненный цикл формальных параметров

Формальные параметры (как по значению, так и по ссылке) ведут себя как локальные переменные, но с некоторыми особенностями в их жизненном цикле:

  • Создание: Формальные параметры создаются в момент вызова подпрограммы. Для параметров по значению выделяется память и в неё копируется значение фактического параметра. Для параметров по ссылке создается ссылка на фактический параметр.
  • Существование: Они существуют и доступны только на протяжении выполнения подпрограммы.
  • Уничтожение: Как только выполнение подпрограммы завершается (происходит выход из неё), формальные параметры (и локальные переменные) уничтожаются, а занимаемая ими память освобождается.

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

Вложенные подпрограммы: принципы работы и область видимости

Одной из мощных особенностей Pascal является возможность объявлять подпрограммы внутри других подпрограмм. Такие подпрограммы называются вложенными.

Принцип вложенности:
Вложенная подпрограмма полностью доступна только внутри той подпрограммы, в которой она была объявлена (родительской). Она невидима и недоступна извне родительской подпрограммы.

Особенности области видимости во вложенных подпрограммах:

  • Вложенная подпрограмма имеет доступ ко всем локальным переменным, константам и параметрам родительской подпрограммы, а также ко всем глобальным переменным программы.
  • Локальные переменные самой вложенной подпрограммы «экранируют» переменные с тем же именем из родительского и глобального окружения.
  • При вызове вложенной процедуры, она получает дополнительный невидимый параметр — указатель фрейма (или статический указатель) родительской процедуры. Этот указатель позволяет вложенной процедуре обращаться к локальным переменным и параметрам вызывающей (родительской) процедуры, создавая так называемую «лексическую область видимости».
program NestedProcedures;

var
  GlobalA: Integer = 100;

procedure OuterProcedure(OuterParam: Integer);
var
  OuterVar: Integer = 10;

  procedure InnerProcedure(InnerParam: Integer);
  var
    InnerVar: Integer = 1;
  begin
    Writeln('  Внутри InnerProcedure:');
    Writeln('    InnerParam = ', InnerParam);    // Параметр InnerProcedure
    Writeln('    InnerVar = ', InnerVar);        // Локальная InnerProcedure
    Writeln('    OuterParam = ', OuterParam);    // Параметр OuterProcedure (доступен)
    Writeln('    OuterVar = ', OuterVar);        // Локальная OuterProcedure (доступна)
    Writeln('    GlobalA = ', GlobalA);          // Глобальная переменная (доступна)
  end; // Конец InnerProcedure

begin
  Writeln('Внутри OuterProcedure:');
  Writeln('  OuterParam = ', OuterParam);
  Writeln('  OuterVar = ', OuterVar);
  Writeln('  GlobalA = ', GlobalA);
  InnerProcedure(5); // Вызов вложенной процедуры
end; // Конец OuterProcedure

begin
  OuterProcedure(20);
end.

Этот пример наглядно демонстрирует, как InnerProcedure может обращаться к переменным OuterParam и OuterVar из родительской процедуры OuterProcedure, а также к глобальной GlobalA.

Опережающее объявление (forward) и его роль

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

  1. Взаимная рекурсия: Две подпрограммы A и B вызывают друг друга. Если A вызывает B, а B вызывает A, то невозможно определить A до B и B до A одновременно.
  2. Логическая структура кода: Программист может захотеть разместить тело подпрограммы в конце раздела описаний для лучшей читаемости, но при этом она должна быть вызвана ранее другими подпрограммами.

Для решения этих проблем используется опережающее объявление с помощью ключевого слова forward.

Механизм forward:
Опережающее объявление состоит из заголовка подпрограммы, за которым следует директива forward. Это объявление сообщает компилятору о существовании подпрограммы и её сигнатуре (имя, параметры, тип результата), позволяя ей быть вызванной до того, как будет определено её тело.

program ForwardExample;

// Опережающее объявление процедур для взаимной рекурсии
procedure ProcA(Value: Integer); forward;
procedure ProcB(Value: Integer); forward;

procedure ProcA(Value: Integer);
begin
  Writeln('ProcA: ', Value);
  if Value > 0 then
    ProcB(Value - 1); // Вызов ProcB, которая уже объявлена (но еще не определена)
end;

procedure ProcB(Value: Integer);
begin
  Writeln('ProcB: ', Value);
  if Value > 0 then
    ProcA(Value - 1); // Вызов ProcA, которая уже объявлена
end;

begin
  ProcA(3);
end.

Важные моменты:

  • После опережающего объявления тело подпрограммы должно быть определено далее в разделе описаний.
  • При определении тела подпрограммы после forward объявления, можно опускать список формальных параметров в заголовке определяющего описания (но это не рекомендуется для ясности и в Free Pascal может требовать полного совпадения в некоторых режимах).
  • В контексте модулей Free Pascal/Lazarus, аналогичная роль «опережающего объявления» выполняется путем размещения прототипа подпрограммы в разделе interface модуля, а её определения — в разделе implementation. Это позволяет другим модулям и основной программе вызывать подпрограмму, не зная деталей её реализации.

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

Лучшие практики проектирования и использования подпрограмм

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

Принцип единственной ответственности (SRP)

Один из важнейших принципов объектно-ориентированного программирования, который применим и к структурному программированию, — это Принцип Единственной Ответственности (Single Responsibility Principle, SRP). Он гласит: «У каждого модуля, класса или, в нашем случае, подпрограммы должна быть только одна причина для изменения».

Применение SRP к подпрограммам:

  • Фокусировка: Каждая подпрограмма должна выполнять одну, четко определенную задачу. Если подпрограмма делает «много всего», это нарушает SRP. Например, подпрограмма ProcessDataAndPrintReport нарушает SRP, так как она одновременно обрабатывает данные и формирует отчет. Лучше разделить её на ProcessData (функция) и PrintReport (процедура).
  • Четкое название: Имя подпрограммы должно точно отражать её единственную ответственность. Если название слишком общее или содержит союзы «и», «или», это может быть сигналом к нарушению SRP.
  • Уменьшение связности (coupling): Подпрограммы, следующие SRP, обычно имеют слабую связность с другими частями системы. Изменение внутренней реализации одной подпрограммы не должно влиять на другие.
  • Улучшение переиспользуемости: Подпрограммы, выполняющие одну конкретную задачу, легче повторно использовать в различных частях программы или в других проектах.
  • Упрощение тестирования: Гораздо проще написать тесты для небольшой подпрограммы с одной ответственностью, чем для крупного блока кода, выполняющего множество задач.
  • Повышение гибкости и поддерживаемости: Когда функциональность четко разделена, программу легче изменять, расширять и отлаживать.

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

procedure ProcessFileAndSaveToDB(FileName: String);
begin
  // 1. Открытие файла и чтение данных
  // 2. Валидация данных
  // 3. Преобразование данных
  // 4. Подключение к базе данных
  // 5. Сохранение данных в БД
  // 6. Закрытие файла
end;

Эта процедура нарушает SRP. Если изменится формат файла, придется менять её. Если изменится схема БД, снова менять её. Лучше разбить:

function ReadDataFromFile(FileName: String): TListOfData;
begin
  // Только чтение и первичная валидация
end;

function TransformData(InputData: TListOfData): TProcessedData;
begin
  // Только преобразование
end;

procedure SaveDataToDB(Data: TProcessedData);
begin
  // Только сохранение в БД
end;

Теперь каждая подпрограмма имеет одну причину для изменения.

Модульность и инкапсуляция

SRP тесно связан с более широкими концепциями модульности и инкапсуляции.

  • Модульность: Использование подпрограмм естественным образом способствует модульности. Разделяя программу на логические блоки, каждый из которых ��нкапсулирует определенную функциональность, мы создаем модули. Эти модули могут быть далее объединены в более крупные единицы (например, в модули Pascal), что значительно упрощает управление сложностью крупных проектов. Модульный подход позволяет команде разработчиков параллельно работать над разными частями программы, минимизируя конфликты.
  • Инкапсуляция: Подпрограммы также способствуют инкапсуляции, то есть сокрытию деталей реализации. Локальные переменные и параметры подпрограммы видны только внутри неё, что защищает их от непреднамеренных изменений извне. Это снижает риск ошибок, поскольку другие части программы не могут напрямую «испортить» внутреннее состояние подпрограммы. Передача параметров по значению и по константе также являются формами инкапсуляции, так как они гарантируют, что исходные данные вызывающей стороны останутся неизменными.

Документирование подпрограмм

«Хороший код самодокументирован» — это популярное утверждение, но оно не отменяет необходимости в надлежащей документации, особенно для подпрограмм, которые используются многими разработчиками или в сложных системах.

Важность документирования:

  • Повышение читаемости: Даже если код хорошо написан, краткое описание назначения подпрограммы, её параметров и возвращаемого значения значительно ускоряет понимание её функционала.
  • Облегчение повторного использования: Документация позволяет другим программистам (или вам самому в будущем) быстро понять, как использовать подпрограмму, не углубляясь в её внутреннюю реализацию.
  • Упрощение поддержки: При необходимости внесения изменений, документация помогает понять, что именно делает подпрограмма и как она должна вести себя.
  • Согласованность: Документация устанавливает ожидания относительно поведения подпрограммы.

Что включать в документацию (комментарии):

  • Назначение: Краткое описание того, что делает подпрограмма.
  • Параметры: Для каждого параметра: имя, тип, назначение и ожидаемые значения (диапазоны, форматы).
  • Возвращаемое значение (для функций): Описание типа и смысла возвращаемого значения.
  • Побочные эффекты: Если подпрограмма изменяет глобальные переменные или var-параметры, это должно быть четко указано.
  • Предусловия (Preconditions): Условия, которые должны быть истинными перед вызовом подпрограммы (например, массив отсортирован, файл существует).
  • Постусловия (Postconditions): Условия, которые будут истинными после успешного выполнения подпрограммы.
// Пример документированной функции
/// <summary>
///   Вычисляет факториал заданного целого числа.
/// </summary>
/// <param name="N">Неотрицательное целое число, для которого вычисляется факториал.</param>
/// <returns>Факториал числа N. Возвращает 1 для N=0.</returns>>
/// <exception cref="ERangeError">Возникает, если N отрицательно.</exception>
function Factorial(N: Integer): Longint;
begin
  if N < 0 then
    raise ERangeError.Create('Факториал определен только для неотрицательных чисел')
  else if N = 0 then
    Result := 1
  else
    Result := N * Factorial(N - 1);
end;

В Pascal нет встроенных средств для генерации документации из комментариев (как Javadoc или Doxygen), но использование стандартизированных блоков комментариев значительно улучшает качество кода.

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

Чтобы проиллюстрировать эффективность подпрограмм, рассмотрим несколько типовых задач:

  1. Поиск максимального элемента в массиве (Функция):
    function FindMax(const Arr: array of Integer): Integer;
    var
      I: Integer;
      MaxVal: Integer;
    begin
      if Length(Arr) = 0 then
        raise Exception.Create('Массив не может быть пустым для поиска максимума');
    
      MaxVal := Arr[0];
      for I := 1 to High(Arr) do
      begin
        if Arr[I] > MaxVal then
          MaxVal := Arr[I];
      end;
      Result := MaxVal;
    end;
    
    // Использование:
    // var MyNumbers: array[0..4] of Integer = (10, 5, 20, 8, 15);
    // Writeln('Максимальное число: ', FindMax(MyNumbers)); // Выведет 20
    
  2. Сортировка массива (Процедура):
    procedure BubbleSort(var Arr: array of Integer);
    var
      I, J, Temp: Integer;
    begin
      for I := 0 to High(Arr) - 1 do
      begin
        for J := 0 to High(Arr) - 1 - I do
        begin
          if Arr[J] > Arr[J+1] then
          begin
            Temp := Arr[J];
            Arr[J] := Arr[J+1];
            Arr[J+1] := Temp;
          end;
        end;
      end;
    end;
    
    // Использование:
    // var DataArray: array[0..4] of Integer = (5, 1, 4, 2, 8);
    // BubbleSort(DataArray);
    // // DataArray теперь (1, 2, 4, 5, 8)
    
  3. Вычисление среднего арифметического (Функция):
    function CalculateAverage(const Numbers: array of Real): Real;
    var
      I: Integer;
      Sum: Real = 0;
    begin
      if Length(Numbers) = 0 then
        Result := 0.0 // Или выбросить исключение
      else
      begin
        for I := 0 to High(Numbers) do
          Sum := Sum + Numbers[I];
        Result := Sum / Length(Numbers);
      end;
    end;
    
    // Использование:
    // var Grades: array[0..2] of Real = (4.5, 3.8, 5.0);
    // Writeln('Средний балл: ', CalculateAverage(Grades):0:2);
    

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

Особенности работы с подпрограммами в Free Pascal и Lazarus

Современная разработка на Pascal чаще всего ведется с использованием Free Pascal — мощного кроссплатформенного компилятора, и Lazarus — интегрированной среды разработки (IDE), которая предоставляет визуальные средства для создания GUI-приложений. Понимание специфики работы подпрограмм в этой экосистеме критически важно для курсовой работы и практического программирования.

Обзор Free Pascal и Lazarus

Free Pascal (FPC) – это свободный (open-source) компилятор языка Pascal, который стремится быть совместимым со стандартами Borland Pascal и Object Pascal (Delphi), а также с ISO Pascal. Его ключевые особенности:

  • Кроссплатформенность: FPC способен компилировать код для огромного количества операционных систем (Linux, Windows, macOS, FreeBSD, DOS, Android, OS/2 и многие другие) и аппаратных платформ (x86, x86_64, ARM, PowerPC, SPARC и др.). Это делает его исключительно универсальным инструментом.
  • Открытый исходный код: Компилятор и его библиотеки доступны для изучения и модификации.
  • Многоцелевой: Подходит для разработки консольных приложений, графических интерфейсов (GUI), динамических библиотек (DLL/shared objects), веб-приложений и многого другого.

Lazarus – это бесплатная, открытая интегрированная среда разработки, которая построена вокруг компилятора Free Pascal. Она является аналогом Delphi и предлагает:

  • Визуальный конструктор форм: Позволяет быстро создавать графические интерфейсы путем перетаскивания компонентов (кнопок, текстовых полей, форм и т.д.) на форму.
  • Мощный редактор кода: С подсветкой синтаксиса, автодополнением, навигацией по коду.
  • Отладчик: Интегрированный отладчик для пошагового выполнения кода, инспекции переменных и точек останова.
  • Библиотека компонентов Lazarus (LCL): Совместима с VCL (Visual Component Library) Delphi и предоставляет богатый набор компонентов для разработки GUI.
  • Полностью написан на Pascal: Сама IDE Lazarus написана на Free Pascal, что демонстрирует мощь и возможности языка.

Перегрузка подпрограмм в Free Pascal

Одним из современных и удобных свойств Free Pascal (как и Object Pascal) является поддержка перегрузки подпрограмм (overloading). Это означает, что в одной и той же области видимости может существовать несколько процедур или функций с одинаковым именем, но разными списками формальных параметров. Компилятор различает их по количеству, порядку и типам параметров, автоматически выбирая нужную подпрограмму при вызове.

Для использования перегрузки в Free Pascal необходимо добавить директиву overload после заголовка каждой перегруженной подпрограммы:

program OverloadExample;

procedure Print(Value: Integer); overload;
begin
  Writeln('Integer: ', Value);
end;

procedure Print(Value: Real); overload;
begin
  Writeln('Real: ', Value:0:2);
end;

procedure Print(Value: String); overload;
begin
  Writeln('String: ', Value);
end;

begin
  Print(10);        // Вызовет Print(Integer)
  Print(3.14159);   // Вызовет Print(Real)
  Print('Hello');   // Вызовет Print(String)
end.

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

Использование модулей для организации подпрограмм

В крупных проектах организация подпрограмм непосредственно в основной программе становится непрактичной. Для этого в Pascal существует концепция модулей (Units) — это самостоятельно компилируемые программные единицы, которые позволяют группировать связанные подпрограммы, константы, типы и переменные в отдельные файлы.

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

Модуль имеет следующую структуру:

unit MyUnit; // Заголовок модуля

interface // Интерфейсная часть: публичный контракт модуля
  // Здесь объявляются константы, типы, переменные и
  // ПРОТОТИПЫ (заголовки) процедур и функций, доступные извне.
  procedure MyPublicProcedure(Param: Integer);
  function MyPublicFunction(Param: Real): Real;

implementation // Реализационная часть: детали реализации
  // Здесь могут быть описаны приватные (для модуля) подпрограммы,
  // а также полные ОПРЕДЕЛЕНИЯ подпрограмм, объявленных в interface.
  procedure MyPublicProcedure(Param: Integer); // Заголовок можно не повторять полностью
  begin
    // Реализация MyPublicProcedure
  end;

  function MyPublicFunction(Param: Real): Real;
  begin
    // Реализация MyPublicFunction
    Result := Param * Param;
  end;

  // Пример приватной процедуры модуля
  procedure MyPrivateHelper;
  begin
    // Эта процедура доступна только внутри MyUnit
  end;

initialization // Необязательный раздел: код, выполняемый при загрузке модуля
  // Например, инициализация переменных
finalization // Необязательный раздел: код, выполняемый при выгрузке модуля
  // Например, освобождение ресурсов
end. // Конец модуля с точкой

Ключевые аспекты использования модулей:

  • Разделение interface и implementation:
    • interface: Содержит только заголовки (прототипы) подпрограмм. Это «публичный» API модуля. Другие программы или модули, подключающие MyUnit, будут «видеть» только эти заголовки.
    • implementation: Содержит полные тела подпрограмм, объявленных в interface, а также любые другие вспомогательные подпрограммы, константы и переменные, которые являются «приватными» для этого модуля.
  • Подключение модулей: Для использования ресурсов модуля в другой программе или модуле используется ключевое слово Uses: uses MyUnit;.
  • Автоматическое подключение SYSTEM: Модуль SYSTEM автоматически подключается к любой программе Free Pascal и содержит все стандартные процедуры и функции языка.
  • Управление памятью в крупных приложениях: Модули в Free Pascal играют важную роль не только в организации кода, но и в структурированном управлении памятью. В отличие от исторической роли модулей в 16-битных системах, где они помогали обходить аппаратные ограничения на сегментацию памяти в 64 Кбайт, в современных 32/64-битных системах модули позволяют пользователям реализовывать собственные менеджеры памяти. Это критически важно для оптимизации работы с большими объемами данных, предотвращения утечек или фрагментации памяти в сложных приложениях. Программист может создать модуль, который предоставляет специализированные функции для выделения и освобождения памяти, адаптированные под конкретные нужды проекта, и затем использовать их вместо стандартных.
  • Файлы .ppu: Скомпилированный модуль Free Pascal имеет расширение .ppu (Pascal Unit), которое содержит объектный код и метаданные.

Отладка подпрограмм в среде Lazarus

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

Основные возможности отладчика Lazarus:

  1. Точки останова (Breakpoints):
    • Установка: Щелкните левой кнопкой мыши по левому полю рядом с номером строки кода. Появится красная точка.
    • Назначение: Программа остановится в этой точке перед выполнением строки.
    • Условные точки останова: Можно задать условие, при котором точка останова сработает (например, if N = 0).
  2. Пошаговое выполнение:
    • Шаг с заходом (Step Into — F7): Выполняет текущую строку. Если строка содержит вызов подпрограммы, отладчик «зайдет» внутрь этой подпрограммы.
    • Шаг без захода (Step Over — F8): Выполняет текущую строку. Если строка содержит вызов подпрограммы, подпрограмма будет выполнена целиком, и отладчик остановится на следующей строке после вызова. Это удобно, когда вы уверены в работе подпрограммы и не хотите просматривать её внутреннюю логику.
    • Выйти из подпрограммы (Step Out — Shift+F8): Продолжает выполнение до выхода из текущей подпрограммы, после чего останавливается на строке, следующей за вызовом этой подпрограммы.
    • Продолжить выполнение (Run — F9): Запускает программу в обычном режиме до следующей точки останова или завершения.
  3. Инспекция переменных (Watches):
    • Во время отладки можно добавлять переменные в окно «Локальные переменные» или «Наблюдение» (Watch window).
    • Это позволяет отслеживать текущие значения параметров, локальных и глобальных переменных по мере выполнения кода.
    • При наведении курсора на переменную в редакторе кода, Lazarus также обычно показывает её текущее значение.
  4. Стек вызовов (Call Stack):
    • Окно «Стек вызовов» показывает последовательность подпрограмм, которые были вызваны до текущей точки остановки. Это крайне полезно для понимания, как программа пришла в определенное состояние.
  5. Окно сообщений отладчика: Показывает отладочную информацию и сообщения об ошибках.

Пример отладки:

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

  1. Установите точку останова на первой строке begin функции CalculateSum.
  2. Запустите программу в режиме отладки (Run -> Run).
  3. Когда программа остановится в точке останова, используйте «Шаг с заходом» (F7), чтобы пошагово выполнять код внутри функции.
  4. В окне «Наблюдение» (или просто наводя курсор) следите за значениями формальных параметров и локальных переменных, чтобы убедиться, что они имеют ожидаемые значения на каждом этапе вычисления.
  5. Если ошибка найдена, можно остановить отладку, исправить код и перезапустить.

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

Заключение

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

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

Особое внимание было уделено механизмам передачи параметров: по значению, по ссылке и по константе. Глубокий анализ этих методов, особенно в контексте оптимизаций компилятора Free Pascal для const-параметров и refcounted типов, позволил понять, как эти механизмы влияют на эффективность и безопасность данных. Мы также разобрали тонкости области видимости и жизненного цикла переменных, а также специфику работы с вложенными подпрограммами и опережающими объявлениями, что критично для управления данными и структурой кода.

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

Наконец, мы погрузились в практические аспекты работы с подпрограммами в среде Free Pascal и Lazarus, рассмотрев такие важные возможности, как перегрузка подпрограмм и использование модулей для структурирования больших проектов, включая их роль в управлении памятью. Отдельное внимание было уделено отладке подпрограмм с использованием мощного инструментария Lazarus, что является незаменимым навыком для каждого разработчика.

Глубокое понимание механизмов пользовательских подпрограмм в Pascal — это не просто академическая задача, а практическая необходимость для любого, кто стремится писать качественный, эффективный и поддерживаемый код. Овладение этими концепциями, особенно с учетом специфики Free Pascal и Lazarus, позволит студентам успешно решать сложные алгоритмические задачи, создавать надежные приложения и заложить прочный фундамент для дальнейшего развития в сфере программирования.

Список использованной литературы

  1. Рапаков Г. Г., Ржеуцкая С. Ю. Программирование на языке Pascal. — СПб.: БХВ-Петербург, 2004. — 480 с.
  2. Окулов С. М. Программирование в алгоритмах. — М.: Бином, 2002.
  3. Ахо А. Д., Хопкрофт Д. Е., Ульман. Структуры данных и алгоритмы. — Москва, Санкт-Петербург, 2000.
  4. Иванов Б. Н. Дискретная математика алгоритмы. Алгоритмы и программы. — Лаборатория базовых знаний, 2003.
  5. Голицына О. Л., Попов И. И. Основы алгоритмизации и программирования. — Москва, 2008.
  6. Подпрограммы: процедуры и функции. — Основы программирования. URL: https://pascal.основы-программирования.рф/podprogrammy-procedury-funkcii (дата обращения: 29.10.2025).
  7. Урок 9 — Процедуры и функции. — Блог программиста. URL: https://maksim.blog/urok-9-procedury-i-funkcii/ (дата обращения: 29.10.2025).
  8. 5 Процедуры и функции. — PASCAL. URL: http://pascal.cgs.ru/pascal/glava5/glava5.htm (дата обращения: 29.10.2025).
  9. Подпрограмма функция. URL: http://informatika.sch86.ru/files/podprogramma_f.htm (дата обращения: 29.10.2025).
  10. Pascal. Подпрограммы: процедуры и функции — 1. — SilverTests.ru. URL: https://silvertests.ru/pascal-subprograms-procedures-and-functions-1/ (дата обращения: 29.10.2025).
  11. Модули в языке Pascal. URL: http://old.cs.msu.ru/sites/default/files/users/alekseev/lecture_06.pdf (дата обращения: 29.10.2025).
  12. Процедуры и функции. URL: http://www.pmtf.org/Files/Pas_book/pas-06.htm (дата обращения: 29.10.2025).
  13. 4 Подпрограммы. — Личный сайт Е. Р. Алексеева. URL: http://alekseev.ucoz.ru/publ/knigi/fpc_lazarus/4_podprogrammy/22-1-0-129 (дата обращения: 29.10.2025).
  14. 5.5.3 Передача аргументов в подпрограмму с использованием параметров-значений и параметров-переменных. URL: http://pascal.cgs.ru/pascal/glava5/glava5_5_3.htm (дата обращения: 29.10.2025).
  15. Изучаем Паскаль. Процедуры и функции. URL: http://pascal.programb.ru/lect_12.html (дата обращения: 29.10.2025).
  16. Процедуры и функции: обзор. — Справка PascalABC.NET. URL: https://pascalabc.net/help/index.html?procedure_function_overview.htm (дата обращения: 29.10.2025).
  17. Теоретический материал (Паскаль): Стандартные функции и процедуры. URL: http://informatika.kgsu.ru/paskal/lektsii/teor_material.html (дата обращения: 29.10.2025).
  18. PASCAL 10. Процедуры и функции в Паскале. Рекурсия. URL: http://www.uchim-vmeste.ru/pascal/urok-10.html (дата обращения: 29.10.2025).
  19. 3.22. Внешние подпрограммы и модули :: 90. — ОСНОВЫ ПРОГРАММИРОВАНИЯ. URL: http://www.osnovy.narod.ru/pascal/p_3_22.htm (дата обращения: 29.10.2025).
  20. Pascal | Лекция №12. — Coderbook главная — Портал программирования. URL: https://coderbook.ru/article/pascal-lekciya-12/ (дата обращения: 29.10.2025).
  21. Пользовательские модули в Паскале. — Кводо.ру. URL: https://kvodo.ru/programmirovanie/pascal/pascal-moduli.html (дата обращения: 29.10.2025).
  22. Процедуры и функции в Pascal. — Информатика | Фоксфорд Учебник. URL: https://foxford.ru/wiki/informatika/procedury-i-funkcii-v-pascal (дата обращения: 29.10.2025).
  23. Подпрограммы, модули и функции в pascal abc.net. URL: https://pascalabcn.net/podprogrammy-moduli-i-funkcii-v-pascal-abc-net/ (дата обращения: 29.10.2025).
  24. Область видимости идентификаторов в Pascal. — Информатика | Фоксфорд Учебник. URL: https://foxford.ru/wiki/informatika/oblast-vidimosti-identifikatorov-v-pascal (дата обращения: 29.10.2025).
  25. Попов Е. А. Экспресс курс программирования в Lazarus Редакция 105 2011 — 2021 год. — FreePascal.ru. URL: https://freepascal.ru/docs/lazarus-course/0-lazarus-course-2011-2021.pdf (дата обращения: 29.10.2025).
  26. 6.4. Вложенные процедуры и функции. — FreePascal.ru. URL: https://freepascal.ru/docs/manual/ref/refsu17.html (дата обращения: 29.10.2025).
  27. Free Pascal и Lazarus. Учебник по программированию. URL: https://www.elib.altstu.ru/elib/books/uvch/fpc_lazarus/fpc_lazarus_ch1.pdf (дата обращения: 29.10.2025).
  28. Среды программирования Free Pascal и Lazarus. — Bstudy. URL: https://bstudy.net/603212/informatika/sredy_programmirovaniya_free_pascal_lazarus (дата обращения: 29.10.2025).
  29. Lazarus Tutorial/ru. — Free Pascal wiki. URL: https://wiki.freepascal.org/Lazarus_Tutorial/ru (дата обращения: 29.10.2025).
  30. Overview of Free Pascal and Lazarus/ru. — Lazarus wiki. URL: https://wiki.freepascal.org/Overview_of_Free_Pascal_and_Lazarus/ru (дата обращения: 29.10.2025).
  31. Руководство программиста Free Pascal. — Документация по FreePascal и Lazarus. URL: https://freepascal.ru/docs/manual/programmers.pdf (дата обращения: 29.10.2025).
  32. Forward (процедурная директива). — Сайт «Всё о Паскале». URL: http://pascal.net.ru/forward (дата обращения: 29.10.2025).
  33. Описание forward. — Turbo Pascal. URL: http://www.turbopascal.net.ru/dir_forward.html (дата обращения: 29.10.2025).
  34. Опережающее объявление. — Справка PascalABC.NET. URL: https://pascalabc.net/help/index.html?forward.htm (дата обращения: 29.10.2025).

Похожие записи