- •4.9.1. Изменяемость
- •4.9.2. Классы операций
- •98 Глава 4
- •4.9.3. Полнота
- •100 Глава 4
- •4.9.5. Операции egual, similar и copy
- •102 Глава 4
- •104 Глава 4
- •5. Исключительные ситуации
- •108 Глава 5
- •110 Глава 5
- •6.2.1. Сигнализация об исключительных ситуациях
- •5.2.2. Обработка исключительных ситуаций
- •112 Глава 5
- •5.2.3. Предложение resignal
- •6.2.4. Необрабатываемые исключительные ситуации
- •114 Глава 5
- •6.2.3. Предложение resignal
- •5.2.4. Необрабатываемые исключительные ситуации
- •116 Глава 5
- •118 Глава 5
- •118 Глава 5
- •120 Глава 5
- •122 Глава 5
- •124 Глава 5
- •128 Глава 6
- •130 Глава 6
- •6.2.1. Реализация итераторов
- •6.2.2. Использование итераторов
- •132 Глава 6
- •134 Глава 6
- •136 Глава 6
- •138 Глава 6
- •140 Глава 7
- •142 Глава 7
- •7.2. Абстракция данных
- •144 Глава 7
- •146 Глава 7
- •148 Глава 7
- •150 Глава 7
- •7.4. Генераторы
- •162 Глава 7
- •154 Глава 7
- •156 Глава 7
- •162 Глава 8
- •164 Глава 8
- •166 Глава 8
- •168 Глава 8
- •170 Глава 9
- •172 Глава 9
- •174 Глава 9
- •176 Глава 9
- •9.1.3. Пример
- •178 Глава 9
- •180 Глава 9
- •182 Глава 9
- •184 Глава 9
- •186 Глава 9
- •188 Глава 9
- •190 Глава 9
- •192 Глава. 9
- •194 Глава 9
186 Глава 9
пирование и отладка
является простым и false в противном случае. При тестировании в качестве первого значения мы используем число 2 и наша программа возвратит правильный ответ — false. Затем мы пробуем число 3 и программа возвратит неверный результат — false. Итак, имеется два результата, на основании которых мы попытаемся сформулировать гипотезу. К одной из возможных и приятных для нас гипотез относится ситуация, при которой после первого теста нам каким-либо образом не удалось реинициализи-ровать программу. Это ведет к тому, что программа всегда работает правильно при первом проходе и всегда неправильно — при последующих. (При работе с языком CLU такая ошибка маловероятна.) Для проверки этой гипотезы мы можем проверить нашу программу с теми же аргументами, однако следует изменить порядок их следования, т. е. сначала мы подставим число 3, а затем — 2. Перед запуском тестов необходимо решить, какой результат подтвердит нашу гипотезу и какой — опровергнет.
1. Результаты, подтверждающие гипотезу (true, false).
2. Результаты, отвергающие гипотезу (false, true), (false,
alse), (true, true)
Пусть в процессе эксперимента программа возвратила сначала значение false, а затем true. Мы сразу же отбрасываем нашу первую гипотезу и выдвигаем другую, например, что программа выполняется неправильно со всеми нечетными' простыми числами.
При отладке очень полезно для начала отыскать такое входное значение, при котором наличие ошибки сразу регистрируется. Это значение не обязательно должно совпадать со значением, позволяющим зарегистрировать ошибку на этапе тестирования. Часто возможно использовать более простое значение, достаточное для того, чтобы выйти на ошибку. Хорошим способом является разделение тестировочных данных на подгруппы и проверка программы с различными подмножествами из этого набора.
Например, предположим, что мы тестируем процедуру palin-drome, приведенную на рис. 9.2, и обнаруживаем, что выполнение ее со знаменитым палиндромом Наполеона «able was I ere I saw elba» приводит к ошибке. Это довольно длинный палиндром, поэтому желательно подобрать более короткий, также выдающий отрицательный результат. Мы начнем с того, что возьмем палиндром, состоящий из одной буквы «г», расположенной в центре исходной фразы, а затем проверим, распознает ли его программа. Если программа не распознает палиндром из одной буквы, то можно предположить, что не распознаются все палиндромы с нечетным числом символов. Если палиндром «г» будет распознан, то мы можем затем попробовать палиндром «ere», исходя из гипотезы, что программа не работает со всеми палиндромами с нечетным числом символов, если это число больше единицы. Если
при случае с «ere» ошибка также не возникнет, то мы, вероятно, ожем попробовать «I ere 1». Предположим, что в этом случае Программа выполнится неправильно. Напрашиваются два пред-доложения: либо ошибка связана с символами пробела, либо 1 использованием букв верхнего регистра. Мы должны теперь Проверить это с кратчайшими палиндромами, например с «» и «1». 1 После того как мы найдем простое входное значение, вызы-1вающее ошибку, мы можем использовать ее для поиска места ^возникновения этой ошибки. Обычно нахождение входных данных, ввызывающих ошибку, является достаточным для определения ее .местоположения. Однако если это не так, то необходимо сузить .область поиска, просматривая для этого промежуточные результаты.
В данном случае наша цель заключается в выделении частей программы, которые не относятся к возникшей проблеме, и последующем подробном анализе оставшихся частей. Мы делаем это при помощи трассировки программы, т. е. запуская ее и анализируя значения отдельных переменных в различных точках. Если программа состоит из нескольких модулей, то наша первая цель — обнаружить, какой модуль служит источником ошибки. Мы делаем это, анализируя все вызовы и возвраты из процедур. (Для итераторов анализируются значения в каждом проходе.) Для каждого вызова производится проверка правильности аргументов. Аргументы должны удовлетворять требованиям, указанным в предложении requires вызываемой процедуры, а также учитывать результаты предыдущих трассировок. Если аргументы не соответствуют требуемым, то ошибку следует искать в вызывающем модуле. В противном случае мы должны выяснить, соответствуют ли получаемые результаты указанным аргументам. Если это не так, то ошибка принадлежит вызываемой процедуре.
Локализация проблемы сведением ее к одной процедуре или циклу часто оказывается достаточной, поскольку мы можем обнаружить ошибку просмотром текста соответствующего модуля. Однако часто полезно бывает выделить в модуле только часть, содержащую ошибку. Для этого мы продолжаем проводить трассировку и анализ локальных переменных данного модуля. В этом случае необходимо обнаружить первое свидетельство неправильного выполнения. В частности, важно проверить соответствие промежуточных результатов значениям, полученным до начала трассировки. Необходимо четко представлять себе, какими должны быть промежуточные результаты.
Рассмотрим неверно составленную реализацию процедуры palindrome:
palindrome = proc (s: string) returns (bool) low: int := I high int := string$size (s)
