План:
Четене на данни и пренасочване на входния и изходния потоци
Симулации
Самостоятелно тестване на функции
Подбор на тестови примери
Оценка на резултатите от тестването
Макрос assert
Трасиране на програмата
Конструкцията
в C и C++
while (cin
>> x)
чете данни от стндартния вход докато не се
появи грешка във входния поток или край на входния поток.
Грешка във входния поток се появява когато:
-- има несъответствие между типа на променливата x и
типа на въведената стойност или
-- когато се въведе специален символ за край на входа (край
на файла).
За операционната система Windows този символ е Ctrl-Z (ASCII
26), за UNIX и MAC OS - Ctrl D (ASCII 04).
Пример: Брой на
думите от входния поток. Дума е низ без интервали.
Разделители между думите са интервали или край на ред (white
space).
// words.cpp* Пренасочване на стандартния вход (обекта cin).
#include <iostream> #include <string> using namespace std; int main() { int count = 0; string word; while (cin >> word) count++; cout << count << " words." << endl; return 0; }
string line;Пример: Брой на редовете във входа.
while (getline(cin, line))
{ process line
}
char ch;Пример: Брой на символите (знаците) във входния поток.
while (cin.get(ch))
{ process ch
}
int main()random.cpp
{ int i;
for (i = 1; i <= 10; i++)
{
int r = rand();
cout << r << "\n";
}
return 0;
}
За генериране на случайни цели числа в интервала [a, b] се използва следната функция:Time now;
int seed = now.seconds_from(Time(0,0,0));
srand(seed);
int rand_int(int a, int b)Пример: Хвърляне на зарове.
{
return a + rand() % (b - a + 1);
}
// dice.cppГенериране на случайни числа тип double в интервала [a, b]:
#include <iostream> #include <string> #include <cstdlib> #include <ctime> using namespace std; /** Sets the seed of the random number generator. */ void rand_seed() { int seed = static_cast<int>(time(0)); srand(seed); } /** Compute a random integer in a range @param a the bottom of the range @param b the top of the range @return a random integer x, a <= x and x <= b */ int rand_int(int a, int b) { return a + rand() % (b - a + 1); } int main() { rand_seed(); int i; for (i = 1; i <= 10; i++) { int d1 = rand_int(1, 6); int d2 = rand_int(1, 6); cout << d1 << " " << d2 << "\n"; } cout << "\n"; return 0; }
double rand_double(double a, double b)
{
return a + (b - a) * rand() * (1.0 / RAND_MAX);
}
void
rand_seed()
/* ЦЕЛ: Инициализира генератора за
случайни числа
*/
{ int seed =
static_cast<int>(time(0));
srand(seed);
}
double rand_double(double a, double
b)
/* ЦЕЛ: намира случайно число в
интервала [a, b]
ПОЛУЧАВА: границите на
интервала
ВРЪЩА: случайно число x,
a <= x и x <= b
*/
{ return a + (b -
a)*rand()*(1.0/RAND_MAX);
}
double deg2rad(double alpha)
/* ЦЕЛ: превръща градуси в
радиани
ПОЛУЧАВА: alpha -
големина на ъгъл в градуси
ВРЪЩА: големината
на ъгъла в радиани
*/
{ const double PI =
3.141592653589793;
return alpha * PI / 180;
}
int main()
{ int NTRIES = 10000;
int i;
int hits = 0;
rand_seed();
for (i = 1; i <=
NTRIES; i++)
{ double ylow =
rand_double(0, 2);
double
angle = rand_double(0, 180);
double
yhigh = ylow + sin(deg2rad(angle));
if
(yhigh >= 2) hits++;
}
cout << "Tries /
Hits = " << NTRIES*(1.0/hits) << "\n";
return 0;
}
** Самостоятелно тестване на функции.
* Данните, с които ще се тества функцията, се получават по 3
начина:
-- от входния поток (от клавиатура или от текстов
файл с пренасочване на входния поток);
-- като стойности, получени от цикъл;
-- случайни числа.
* Примери за тестване на функцията squareroot за намиране на
квадратен корен по метода на Херон (Babylonian
method).
x0 = a,
xn+1
= ( xn + a/xn ) / 2, n
= 0,1, 2, 3, ...
Първи пример - данните идват от входния
поток:
// sqrtest1.cpp
#include <iostream>
#include <cmath>
using namespace std;
/**
Tests whether two floating-point numbers are
approximately equal.
@param x a floating-point number
@param y another floating-point number
@return true if x and y are approximately equal
*/
bool approx_equal(double x, double y)
{
const double EPSILON = 1E-14;
if (x == 0) return fabs(y) <= EPSILON;
if (y == 0) return fabs(x) <= EPSILON;
return fabs(x - y) / max(fabs(x), fabs(y)) <= EPSILON;
}
/* Function to be tested */
/**
Computes the square root using Heron's formula
@param a an integer >= 0
@return the square root of a
*/
double squareroot(double a)
{
if (a == 0) return 0;
double xnew = a;
double xold;
do
{ xold = xnew;
xnew = (xold + a / xold) / 2;
}
while (!approx_equal(xnew, xold));
return xnew;
}
/* Test harness */
int main()
{
double x;
while (cin >> x)
{
double y = squareroot(x);
cout << "squareroot of " << x << " = " << y << "\n";
}
return 0;
}
25 squareroot of 25 = 5 3 squareroot of 3 = 1.73205 q |
Втори пример - входните стойности на функцията се генерират от цикъл.
// sqrtest2.cpp
/* Test harness */
int main()
{
double x;
for (x = 0; x <= 10; x = x + 0.5)
{ double y = squareroot(x);
cout << "squareroot of " << x << " = " << y << "\n";
}
return 0;
}
squareroot of 0 = 0 squareroot of 0.5 = 0.707107 squareroot of 1 = 1 squareroot of 1.5 = 1.22474 squareroot of 2 = 1.41421 |
Трети пример - входните
стойности се получават от генератор за случайни числа.
// sqrtest3.cpp
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <ctime>
using namespace std;
/**
Sets the seed of the random number generator.
*/
void rand_seed()
{
int seed = static_cast<int>(time(0));
srand(seed);
}
/**
Compute a random floating point number in a range
@param a the bottom of the range
@param b the top of the range
@return a random floating point number x,
a <= x and x <= b
*/
double rand_double(double a, double b)
{
return a + (b - a) * rand() * (1.0 / RAND_MAX);
}
/**
Tests whether two floating-point numbers are
approximately equal.
@param x a floating-point number
@param y another floating-point number
@return true if x and y are approximately equal
*/
bool approx_equal(double x, double y)
{
const double EPSILON = 1E-14;
if (x == 0) return fabs(y) <= EPSILON;
if (y == 0) return fabs(x) <= EPSILON;
return fabs(x - y) / max(fabs(x), fabs(y)) <= EPSILON;
}
/* Function to be tested */
/**
Computes the square root using Heron's formula
@param a an integer >= 0
@return the square root of a
*/
double squareroot(double a)
{
if (a == 0) return 0;
double xnew = a;
double xold;
do
{
xold = xnew;
xnew = (xold + a / xold) / 2;
}
while (!approx_equal(xnew, xold));
return xnew;
}
/* Test harness */
int main()
{
rand_seed();
int i;
for (i = 1; i <= 100; i++)
{
double x = rand_double(0, 1E6);
double y = squareroot(x);
cout << "squareroot of " << x << " = " << y << "\n";
}
return 0;
}
squareroot of 185949 =
431.218 squareroot of 680715 = 825.055 squareroot of 17883.8 = 133.73 squareroot of 238868 = 488.742 |
** Подбор на
тестови примери.
1. Докато се пише програмата, трябва да имаме прост тестов пример,
на който знаем решението.
2. Програмата се проверява с други тестови примери, също с
известни решения - позитивни тестове.
3. Включват се и граничните случаи.
- За функцията squareroot това са 0,
големи числа (напр. 1Е20)
и числа, близки до 0 (напр. 1е-20).
Целта е да се определят границите на параметрите, за които
функцията работи вярно.
4. Функцията се проверява с негативни тестови примери - некоректни
входни данни.
- Такива за squareroot са отрицателни
стойности на параметъра.
Използване на файл за запазване на тестовия вход и изпълнение с
пренасочване на входния и изходния потоци.
>sqrtest1 < test.in > test.out
// sqrtest4.cpp* За оракул можем да използваме стандартната аритметична функция pow(x,0.5)или функцията sqrt(x).
/* Test harness */
int main()
{
int i;
for (i = 1; i <= 100; i++)
{
double x = rand_double(0, 1E6);
double y = squareroot(x);
if (!approx_equal(y * y, x)) cout << "Test failed. ";
else cout << "Test passed. ";
cout << "squareroot of " << x << " = " << y << "\n";
}
return 0;
}
Функциите често съдържат
неявни предположения - напр. знаменатели трябва да са различни от
нула, заплатите не трябва да бъдат отрицателни и т.н.
void raise_salary(Employee& e, double by)Ако условието не е изпълнено, програмата завършва с полезно съобщение за грешка и показва номера на реда в текста на програмата.
{
assert(e.get_salary() >= 0 );
assert(by >= -100);
double new_salary = e.get_salary() * (1 + by / 100);
e.set_salary(new_salary);
}
assertion failed in file finclac.cpp line 61: by >= -100Това е сигнал, че нещо се е объркало другаде и че програмата се нуждае от по-нататъшно тестване.
Пример: Сложна лихва.
// futval0.cpp
#include <iostream>
#include <cmath>
#include <cassert>
using namespace std;
fincalc.cpp:8: failed
assertion `nyear>=0 && p>=0' abnormal program termination |
string int_name(int n)[trace.cpp]
{ cout << "Entering digit_name. n = " << n << "\n";
...
cout << "Exiting digit name. Return value = "
<< s << "\n";
return s;
}