- •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
178 Глава 9
9.1.4. Тестирование итераторов
Составление тестов для итераторов эквивалентно составлению тестов для процедур. Единственное любопытное отличие заключается в том, что маршруты в спецификациях итераторов сходны с маршрутами для циклов. Другими словами, мы должны быть уверены в том, что в набор тестов включены случаи, когда итерация выполняется дважды и трижды, а также, если это возможно, не выполняется ни разу. Например, рассмотрим итератор primes:
primes = iter (n: int) yields (int)
effects Если n^ 2, то выдаются все простые числа, меньшие или равные n; в противном случае не выдается ничего.
Тесты должны включать случаи для n, равного 1, 2 и 3. Необходимость в большем числе тестов определяется на основе реализации итератора.
9.1.6. Тестирование типов данных
При тестировании типов данных мы, как обычно, создаем тесты на основе анализа спецификации и реализации каждой из операций. Теперь мы должны проверить операции не по отдельности, а целыми группами, поскольку некоторые операции (конструкторы и примитивные конструкторы) создают объекты, используемые при тестировании других операций. Например, в операциях intset конструкторы create, insert и delete должны быть использованы для генерации аргументов для других операций и друг для друга. (Спецификация для intset повторена на рис. 9.3.) intset = data type is create, insert, delete, member, size, elements Назначение
Наборы intset представляют собой неограниченные математические наборы целых чисел. Эти наборы изменяемы: операции insert и delete добавляют и удаляют из набора числа. Операции create == proc ( ) returns (intset)
effects Возвращает новый пустой набор intset. insert == proc (s: intset, x: int)
modifies s effects Добавляет x к элементам s; после завершения операции, spost ==
= s (J {x), где Spost ^ть набор значений в s при возврате из intset. delete s= proc (s: intset, x: int)
modifies s
effects Удаляет x из s (т.е. Spogi == s — (x})« member == proc (s: intset, x; int) returns (tool) ; effects Возвращает (x ^ s). 'size = proc (s: intset) returns (int)
effects Возвращает число элементов в s. elements = iter (s: intset) yields (int) requires s не модифицируется телом цикла.
effects Создает влементы s, по одному и в произвольном порядке. vend Intset Рис. 9,3. Спецификация intset.
Уестирование и отладка
Мы начнем с просмотра маршрутов в спецификациях. Мар-^щруты в спецификациях операций member и elements очевидны. Для операции member мы должны включить случаи, которые выдают в качестве результата true и false. Поскольку elements является итератором, мы должны просмотреть по крайней мере маршруты длиной нуль, единица и двойка. Следовательно, нам необходимы наборы intset, содержащие нуль, один и два элемента. Пустой набор intset и одноэлементный набор intset должны также проверять граничные условия. Итак, мы можем начать со следующих наборов: 1) пустой набор intset, получаемый обращением к intset$create ( ); 2) одноэлементный набор intset, полученный размещением числа 3 в пустом наборе intset; 3) двухэлементный набор intset, получаемый размещением в пустом наборе чисел 3 и 4.
Для каждого из них мы должны выполнить обращения к member, size и elements, а затем проверить результаты. В случае с member мы должны сделать обращения, для которых элемент принадлежит набору, а в остальных — не принадлежит.
Очевидно, что этих случаев недостаточно. Например, операция delete вовсе не проверяется, а в других спецификациях имеются неучтенные нами маршруты. Эти маршруты неким образом скрыты в наших неформальных спецификациях. При использовании формальных спецификаций они стали бы более очевидны.
При размещении элемента, уже принадлежащего набору, размер набора intset остается прежним, следовательно, необходимо проанализировать случай, когда мы дважды помещаем в набор intset один и тот же элемент. Аналогичным образом, размер набора intset уменьшается в том случае, если имеющийся в нем элемент удаляется. Следовательно, необходимо учесть ситуацию, в которой элемент удаляется после его размещения и удаляется элемент, не принадлежащий набору. Мы можем добавить следующие дополнительные наборы: 1) набор, полученный из пустого набора двукратным размещением в нем числа 3; 2) набор, полученный размещением числа 3 и его последующим удалением; 3) набор, полученный размещением числа 3 и удалением числа 4.
Для нахождения скрытых маршрутов необходимо отдельно выделить маршруты для конструкторов. Операция insert должна работать правильно вне зависимости от того, принадлежал ли уже вновь размещаемый элемент набору или нет. Это относится также и к операции delete. Такой подход дает три только что рассмотренных случая.
Разумеется, мы должны дополнительно рассмотреть маршруты в реализациях операций. Рассмотренные случаи покрывают реализации, использующие массив без дублирующих элементов (рис. 9.4). Единственная возможная проблема связана с содержащей цикл операцией member. Для учета всех маршрутов в этом
м
