Побочный эффект.

Замечание

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

 

Написание подпрограмм с полными спецификациями преследует две цели. Во-первых, подпрограмма становится самостоятельной единицей, которую можно считать новой командой виртуальной машины, реализуемой с помощью ЯВУ. При этом подпрограмма с полной спецификацией может рассматриваться как ''черный ящик``, т.е. можно абстрагироваться от тела подпрограммы и рассматривать ее как функциональный узел с одним действием. Такой способ алгоритмической абстракции называется абстракцией через спецификацию.

 

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

 

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

 

 

Пусть была задана спецификация {P}f{Q} и написана подпрограмма .

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

 

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

 

function sqrt(x : extended):extended;

{Вычисляет квадратный корень из аргумента x>0}

 

function Probel(s : string):byte;

{Вычисляет количество пробелов в строке s}

 

Значения таких функций часто используются в выражениях без копирования в какие-либо переменные. Например,

 

y:=sqrt(x)+x;

 

if Probel(s)>2 then ...

 

Проблемы возникают, например, при использовании функций с побочным эффектом в логических выражениях, участвующих в условных операторах или условиях циклов, т.к. в Паскале возможны две модели вычисления логических выражений - полная или неполная. Теперь представим себе, что у нас есть функция с побочным эффектом f(x), возвращающая результат целого типа и увеличивающая значение параметра x, передаваемого по ссылке, на 1. Пусть в программе есть условный оператор

 

if (a[i]<2) and (f(x)>3) then

...

else

...

 

Если выполняется неполное вычисление логических выражений, то, в зависимости от значения i-го элемента массива a, после выполнения этого оператора значение x то будет меняться, то нет. Как видим, изменится x или нет не зависит от самого x.

 

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

 

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

 

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