Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Програмирование. Структурное програмирование, подпрограммы, строки. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
щий модуль. В других случаях «заглушка» может выдавать значения, заданные заранее или вычисленные по упрощенному алгоритму.
Таким образом, сначала создается логический скелет программы,
который затем обрастает плотью кода.
Можно применять к процессу программирования восходящую тех­нологию – написать и отладить сначала модули нижнего уровня, а за­тем объединять их в более
крупные фрагменты, но этот подход имеет
ряд недостатков.
Во-первых, в процессе кодирования верхнего уровня могут быть вскрыты те или иные трудности проектирования более низких уровней программы (просто потому, что при написании программы ее логика продумывается более тщательно, чем при проектировании). Если по­добная ошибка обнаруживается в последнюю очередь, требуются до полнительные затраты на переделку уже готовых модулей нижнего уровня.
Во-вторых, для отладки каждого модуля, а затем более крупных фрагментов программы требуется каждый раз составлять свои тесто­вые примеры, и программист часто вынужден имитировать то окруже­ние, в котором должен работать модуль. Нисходящая же технология программирования обеспечивает естественный порядок создания
те-
стов – возможность нисходящей отладки, которая рассмотрена далее.
Этапы проектирования и программирования совмещены во време­ни: в идеале сначала проектируется и кодируется верхний уровень, за­тем – следующий и так далее. Такая стратегия применяется потому, что в процессе кодирования может возникнуть необходимость внести изменения, отражающиеся на модулях нижнего уровня.
Нисходящее тестирование. Этот
этап записан последним, но это
не значит, что тестирование не должно проводиться на предыдущих этапах. Проектирование и программирование непременно должны со­провождаться написанием набора тестов – проверочных исходных данных и соответствующих им наборов ожидаемых результатов.
Необходимо различать процессы тестирования и отладки программы.
Тестирование – это процесс выполнения программ с целью обна­ружения
логических ошибок.
Логической ошибкой считается любое несоответствие работы про­граммы заданной на нее спецификации.
Хорошим считается тест, который имеет высокую вероятность об­наружения еще не выявленной ошибки. Удачным считается тест, кото­рый обнаруживает еще не выявленную ошибку.
71
-
Отладка программы – это процесс, осуществляемый после удачно­го выполнения теста. Процесс отладки начинается при обнаружении ошибки (например, при удачном завершении теста) и проводится в два этапа:
определяются природа и местонахождение подозреваемой ошибки в программе;
фиксируется или исправляется ошибка.
Отладка – процесс исправления ошибок в программе, при этом цель исправить
все ошибки не ставится. Исправляют ошибки, обнару­женные при тестировании. При планировании следует учитывать, что процесс обнаружения ошибок подчиняется закону насыщения, т. е. большинство ошибок обнаруживается на ранних стадиях тестирова­ния, и чем меньше в программе осталось ошибок, тем дольше искать каждую из них.
Для исчерпывающего тестирования программы необходимо прове-
рить
каждую из ветвей алгоритма. Общее число ветвей определяется комбинацией всех альтернатив на каждом этапе. Это конечное число, но оно может быть очень большим, поэтому программа разбивается на фрагменты, после исчерпывающего тестирования которых они рас­сматриваются как элементарные узлы более длинных ветвей. Кроме данных, обеспечивающих выполнение операторов в требуемой после­довательности,
тесты должны содержать проверку граничных условий (например, переход по условию х > 10 должен проверяться для значе­ний, больших, меньших и равных 10). Отдельно проверяется реакция программы на ошибочные исходные данные.
Идея нисходящего тестирования предполагает, что к тестированию программы приступают еще до того, как завершено ее проектирование. Это позволяет раньше опробовать основные межмодульные
интерфей­сы, а также убедиться в том, что программа в основном удовлетворяет требованиям пользователя. Только после того как логическое ядро ис­пытано настолько, что появляется уверенность в правильности реали­зации основных интерфейсов, приступают к кодированию и тестиро­ванию следующего уровня программы.
72
КОНТРОЛЬНЫЕ ВОПРОСЫ
1. Что такое структурное программирование?
2. В чем достоинство структурного программирования?
3. В чем смысл принципа «разделяй и властвуй» применительно к
разработке программ?
4. В чем смысл принципа «иерархического упорядочения» приме-
нительно к разработке программ?
5. В чем смысл принципа «слабой связанности» (Low Coupling)
применительно к разработке программ?
6. В
чем смысл принципа «сильного сцепления» (High Cohesion)
применительно к разработке программ?
7. Какие конструкции относятся к базовым конструкциям струк-
турного программирования?
8. Какие задачи решаются на этапе «анализ требования»?
9. Какие задачи решаются на этапе «проектирование»?
10. Какие задачи решаются на этапе «структурное кодирование»?
11. Какие задачи решаются на этапе «нисходящее тестирование»?
12.
Что такое тестирование программ?
13. Что такое отладка программ?
14. Для чего используют IPO-диаграммы?
73
Приложение
ПРАКТИЧЕСКИЕ ЗАДАНИЯ ДЛЯ ЗАКРЕПЛЕНИЯ
Тема. ПОДПРОГРАММЫ
Задачи
1. Написать и протестировать функцию. Функция получает символ, изображающий шестнадцатеричную цифру, возвращает соответству­ющее ей целое число.
2. Написать и протестировать функцию. Функция получает целое число, соответствующее шестнадцатеричной цифре, возвращает соот­ветствующий ей символ.
3. Написать и протестировать функцию. Функция получает целое неотрицательное число и основание системы счисления его р-ичное представление (p принадлежит интервалу от 2 до 16).
4. Написать и протестировать процедуру. Процедура упорядочива- ет значения трех вещественных переменных x, y, z следующим обра­зом: x <= y <= z.
5. Написать и протестировать функцию. Функция получает три ве- щественных значения и возвращает наибольшее из них.
6. Написать и протестировать функцию. Функция получает два не- отрицательных тель (НОД).
7. Написать и протестировать функцию. Функция получает неот- рицательное вещественное число с нулевой целой частью и возвращает его представление в системе счисления с основанием р (p принадлежит интервалу от 2 до 16).
8. Написать и протестировать функцию. Функция получает строку, содержащую представление р-ичного ствующее ему число.
целых числа и возвращает их наибольший общий дели-
целого, возвращает соответ-
74
p, возвращает
9. Написать и протестировать функцию. Функция получает строку,
содержащую представление дробной части р-ичного числа (p принад­лежит интервалу от 2 до 16), возвращает соответствующее ей веще­ственное значение.
10. Написать и протестировать функцию. Функция получает неот- рицательное десятичное целое значение и номер его разряда, возвра­щает значение разряда целого числа по номеру.
Нумерация разрядов
справа налево, начиная с 1.
Примеры решений
Задача 3
Условие
Функция получает целое неотрицательное число и основание си­стемы счисления p, возвращает его р-ичное представление (p принад­лежит интервалу от 2 до 16).
Решение
В соответствии с заданием функция получает в качестве исходных значений два значения целого типа и возвращает строковое значение. Поэтому
в заголовке функции опишем два формальных параметра­значения целого типа (integer), а в качестве типа результата, возвраща­емого функцией, выберем строковый тип (string). Организуем вычис­ление p-ичного представления неотрицательного целого числа n с по­мощью управляющей структуры WHILEDO. Для нахождения значений разрядов p-ичного представления неотрицательного десятичного цело­го числа n необходимо это число делить на основание
системы счисле­ния (p) и брать в качестве значения текущего двоичного разряда оста­ток от деления, частное от деления заносится в n и берется в качестве делимого для следующего шага. Очевидно, что для значений n от n = 0 и до n = p – 1 цикл не надо выполнять ни разу, потому что 0, 1, …, p – 1 – p-ичные цифры. Поэтому условием выполнения цикла будет утверждение: n > p – 1. В теле цикла (в do-части) каждый раз будем формировать значение текущего двоичного разряда b = n mod p, дели­мого n = n div p для следующего шага, и добавлять значение текущего p-ичного разряда b, преобразованное в символьный формат с помощью функции NumTodig, к строке результата слева. Значение старшего раз­ряда будет последним частным от деления, которое будет содержаться в n после
выхода из цикла. При описании этой последовательности
75
действий с помощью управляющей структуры WHILEDO получим ал­горитм, блок-схема которого представлена на рисунке ниже.
1
n – десятичное целое число b – значение текущего
разряда p-ичного представления n р – основание системы счисления s – строка, содержащая р-ичное представление числа n
s:= ‘’
3
b:= n mod p n:= n div p s:=
2
NumTodig(b)+s
n > p-1
И
4
s:=
NumTodig(n)
+ s
Пример программы, работающей в режиме консольного приложе­ния Delphi, реализующей приведенную блок-схему с помощью двух функций NumToDig и NumToPStr, приведен ниже.
program PIntToP;
{$APPTYPE CONSOLE}
uses SysUtils; //-----------------описание функции--------------------­function NumToDig(d: integer): Char; //функция получает целое число, возвращает соответствую­щую ему цифру begin case d of
76
0..9: Result:= Chr(d + Ord('0'));
10..15: Result:= Chr(d + Ord('A') - 10);
else Result:= Chr(0);
end;
end;
//-----------------описание функции--------------------­function NumToPStr(n,p: integer): String; //функция получает целое число, возвращает соответствую­щее p-ичное представление var b: cardinal;//значение текущего двоичного разряда begin
while n > p - 1 do begin b:= n mod p; n:= n div p; result:= NumToDig(b) + result; //вызов функции end;
result:= NumToDig(n) + result; //вызов функции end; //-------------------------------------------------­var
n: integer;//десятичное целое без знака
p: integer = 16;//основание системы счисления
s: String; begin write('n = '); readln(n); //-------------------вызов функции------------­ writeln('PStr = ',NumToPStr(n,p)); //-------------------вызов функции------------­ s:= NumToPStr(n,p); writeln('PStr = ',s); readln; end.
Тестирование функции
Будем тестировать функцию методом тестирования путей. Среди возможных путей выполнения функции можно выделить два пути, ко­торые представлены на Е-схеме выполнения функции, полученной из блок-схемы.
77
3
второй путь
1
2
первый путь
4
По первому пути цикл не выполняется ни разу. Второй путь вклю­чает в себя множество путей, на которых цикл выполняется один и бо­лее раз.
Необходимо подобрать исходные данные для выполнения про­граммы по каждому из возможных путей.
Набор тестов для тестирования разработанной функции представ­лен в таблице.
Номер
теста
1 n = 0, p = 2 0 Цикл не выполняется 2 n = 1, p = 2 1 Цикл не выполняется 3 n = 8, p = 2 1000 Цикл выполняется три раза 4 n = 0, p = 16 0 Цикл не выполняется 5 n = 10, p = 16 A Цикл не выполняется 6 n = 15, p = 16 F Цикл не выполняется 7 n = 256, p = 16 100 Цикл выполняется два раза
Исходные данные
Ожидаемый
результат
Путь прохождения
Задача 4
Условие
Написать и тестировать процедуру. Процедура упорядочивает зна­чения трех вещественных переменных x, y, z следующим образом:
x <= y <= z.
Решение
Так как процедура должна в результате своей работы изменять зна­чения переданных ей фактических параметров, ее формальные пара­метры необходимо описать как параметры-переменные. Для упорядо­чения значений переменных x, y, z в порядке (x <= y <= z) можно
78
Л
x > y
В этой точке тождественно истинно утверждение x = min(x, y)
И
Exchange(x, y)
Л
Л
x > z
В этой точке тождественно истинно утверждение x = min(x, y, z)
y > z
В этой точке тождественно истинно утверждение x <= y <= z
И
Exchange(x, z)
И
Exchange(y, z)
Блок-схема алгоритма
поступить так: найти минимальное из трех значений и занести его в x; затем из оставшихся двух значений найти максимальное и занести его в z. Сначала найдем минимальное значение из трех, с этой целью, например, можно сравнить x и y. Если x – большее из них, то перемен-
79
ные обмениваются значениями, в противном случае никаких действий выполнять не надо. После этого x будет содержать наименьшее из двух значений x, y. Затем необходимо сравнить x и z. Если x – большее из них, то переменные обмениваются значениями, в противном случае никаких действий выполнять не надо. После этого x будет содержать наименьшее из трех значений x, y, z. Теперь необходимо занести
в z максимальное значение из оставшихся двух. Для этого сравним значе­ния y и z. Если y – большее из них, то переменные обмениваются зна­чениями, в противном случае никаких действий выполнять не надо. Теперь мы имеем следующее упорядочение значений переменных: x <= y <= z. Для описания этой последовательности действий будем использовать последовательность из трех управляющих структур
IFTHEN. Поскольку в
THEN-частях всех управляющих структур
IFTHEN обмениваются значениями две переменные, организуем для
этой цели процедуру Exchange. Так как она должна в результате своей работы изменять значения переданных ей фактических параметров, ее формальные параметры необходимо описать как параметры-пере­менные.
Пример программы, работающей в режиме консольного приложе-
ния Delphi, реализующей приведенную блок-схему с помощью
двух
процедур Exchange и Reorder3, показан ниже.
program PReorder3;
{$APPTYPE CONSOLE}
uses SysUtils; //----------описание процедуры-------------­procedure Exchange( var a,b: real); var t: real; begin t:= a; a:= b; b:= t; end; //-----------описание процедуры--------­procedure Reorder3( var x,y,z: real); begin
80
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]