Если вы написали несколько задач и подготовили для них тесты, вероятно, вы будете крайне неприятно себя чувствовать, если какие-то из тестов окажутся некорректными (в том смысле, что они не будут согласованы с условием задачи): значение какой-то величины будет больше допустимой верхней границы для нее, граф не будет удовлетворять требованиям связности или же не будет являться деревом... Это естественно, что вы будете себя так чувствовать. Даже опытные авторы задач не застрахованы от ошибок (это случается даже на самых престижных соревнованиях: пример тому — финал чемпионата мира ACM ICPC 2007 г.)
Настоятельно рекомендуется писать специальную программу (называемую валидатором), чтобы формально проверить каждый тест на соответствие всем требованиям условия задачи. Валидаторы обязательны для задач, которые готовятся для Codeforces. Polygon имеет встроенную поддержку валидаторов.
Написать валидатор с помощью testlib.h на самом деле очень легко.
Пример
Ниже приводится валидатор, который была написан для задачи 100541A - Stock Market:
#include "testlib.h"
int main(int argc, char* argv[]) {
registerValidation(argc, argv);
int testCount = inf.readInt(1, 10, "testCount");
inf.readEoln();
for (int i = 0; i < testCount; i++) {
int n = inf.readInt(1, 100, "n");
inf.readSpace();
inf.readInt(1, 1000000, "w");
inf.readEoln();
for(int i = 0; i < n; ++i) {
inf.readInt(1, 1000, "p_i");
if (i < n-1)
inf.readSpace();
}
inf.readEoln();
}
inf.readEof();
}
Самое замечательное в этом валидаторе то, что он очень простой, и в нем очень трудно написать что-то неправильно.
В репозитории Github можно найти другие примеры валидаторов.
Функции и методы
Первая строка вашего кода должна содержать вызов registerValidation()
: немного магии, и вы можете использовать необходимые методы. Большинство методов для валидатора начинаются с префикса read и именно выполняют чтение: перемещают указатель во входном потоке на следующую позицию после прочтения чего-либо. В процессе чтения обнаруживаются нарушения (входные данные не соответствуют тому, что вы пытаетесь прочитать: например, вы предпринимаете попытку прочитать целое число, а во входных данных встречается строка) и выбрасывается ошибка.
Замечания:
- Валидатор строг. Он проверяет корректное расположение пробелов. Например, последовательность вызовов вида прочесть число, прочесть пробел, прочесть число гарантирует наличие ровно одного пробела между числами; в противном случае валидатор сообщит об ошибке.
- Некоторые методы частично поддерживают синтаксис регулярных выражений. Конечно, это не полноценные регулярные выражения, которые вы можете использовать во многих языках программирования. Это очень простая версия, в которой поддерживается следующее:
- Множество символов: например,
[a-z]
— любые строчные латинские буквы,[^a-z]
— любые символы за исключением строчных латинских букв. - Диапазон, например, шаблон
[a-z]{1,5}
описывает строки длиной от 1 до 5 символов, содержащие только строчные латинские буквы. - Оператор Или, например, шаблон
mike|john
— это или строкаmike
, или строкаjohn
. - Необязательные символы, например, шаблон
-?[1-9][0-9]{0,3}
допускает ненулевые целые числа от -9999 до 9999 (обратите внимание на необязательный знак "минус"). - Повторения, например, шаблон
[0-9]*
допускает последовательности (как пустые, так и непустые) цифр, а шаблон[0-9]+
только непустые последовательности цифр.
- Множество символов: например,
- Также заметим, что при распознавании регулярных выражений используется очень простой жадный алгоритм. Например, шаблон
[0-9]?1
не допускает1
в силу жадного поведения распознавателя.
Ниже представлен полный список функций и методов
Метод / функция | Что делает |
---|---|
void registerValidation() | Эта функция должна быть вызвана в начале вашего кода, чтобы использовать валидатор. После вызова этой функции вы получаете доступ к входному потоку посредством переменной inf . |
char readChar() | Этот метод возвращает текущий символ и перемещает указатель на один символ вперед. |
char readChar(char c) | Аналогичен readChar() , но обеспечивает проверку, что прочитанный символ именно c . |
char readSpace() | Аналогичен readChar(' ') . |
void unreadChar(char c) | Возвращает символ c во входной поток. |
string readToken() | Читает и возвращает очередную лексему (токен). |
string readToken(string regex) | Аналогичен readToken() , но выполняет проверку соответствия лексемы (токена) указанному регулярному выражению regex . |
string readWord() | Аналогичен readToken() . |
string readWord(string regex) | Аналогичен readToken(string regex) . |
long long readLong() | Читает и возвращает длинное целое (long long в C/C++ и long в Java) |
long long readLong(int L, int R) | Аналогичен readLong() , но выполняет проверку, что значение находится в диапазоне [L, R] (включительно) |
int readInt(), int readInteger() | Читает и возвращает целое число (тип int как в Java, так и в C/C++) |
int readInt(int L, int R), int readInteger(L, R) | Аналогичны readInt() , но выполняет проверку, что значение находится в диапазоне [L, R] (включительно) |
double readReal(), double readDouble() | Читают и возвращают вещественное число (double ). |
double readReal(double L, double R), double readDouble(double L, double R) | Аналогичны readReal() , readDouble() , но выполняют проверку, что значение находится в диапазоне [L, R]. |
double readStrictReal(double L, double R, int minPrecision, int maxPrecision), double readStrictDouble(double L, double R, int minPrecision, int maxPrecision) | Аналогичны readReal(L, R) , readDouble(L, R) , но выполняют дополнительную проверку, что количество цифр после десятичной точки находится в диапазоне [minPrecision, maxPrecision]. Экспоненциальная запись числа или другие нестандартные формы записи не допускаются. |
string readString(), string readLine() | Прочитывают строку, начиная с текущей позиции до EOLN. Перемещают указатель во входном потоке на первый символ следующей строки (если она существует). |
string readString(string regex), string readLine(string regex) | Аналогичны readString() and readLine() , но выполняют проверку, что строка соответствует указанному регулярному выражению regex . |
void readEoln() | Читает EOLN или завершает работу с ошибкой. Заметим, что этот метод чудесным образом работает как для Windows, так и для Linux. В Windows он прочитывает #13#10, а в Linux #10. |
void readEof() | Читает EOF или завершает работу с ошибкой. |
Параметр variableName
Рекомендуется использовать последний строковый параметр variableName в методах readInt/readInteger/readLong/readDouble/readWord/readToken/readString/readLine, чтобы сделать сообщение об ошибке более удобным для чтения. Т.е. предпочтительнее использовать inf.readInt(1, 100, n)
вместо inf.readInt(1, 100)
. При возникновении ошибки в первом случае будет выводиться сообщение вида FAIL Integer parameter [name=n] equals to 0, violates the range [1, 100]
.
Использование ensure/ensuref
Чтобы проверить некоторые требования (например, то, что граф не содержит петель, т.е. что xi ≠ yi), используйте ensuref(x_i != y_i, "Graph can't contain loops")
. Допускается использование спецификаторов формата языка C, подобных ensuref(s.length() % 2 == 0, "String 's' should have even length, but s.length()=%d", int(s.length()))
. Также вы можете использовать более простую форму ensure(x > y)
, в этом случае будет печататься нарушенное условие, если оно не выполняется: FAIL Condition failed: "x > y"
.
Ссылки: страница testlib.h на Github