План:
Четене на данни и пренасочване на входния и изходния потоци
Симулации
Самостоятелно тестване на функции
Подбор на тестови примери
Оценка на резултатите от тестването
Макрос assert
Трасиране на програмата
Конструкцията
в C и C++
while (cin
>> x)
чете данни от стндартния вход докато не се
появи грешка във входния поток или край на входния поток.
Грешка във входния поток се появява когато:
-- има несъответствие между типа на променливата x и
типа на въведената стойност или
-- се въведе специален символ за край на входа (край на
файла).
За операционната система Windows този символ е Ctrl-Z (ASCII
26), за UNIX и MAC OS - Ctrl D (ASCII 04).
Пример: Брой на
думите от входния поток. Дума е низ без интервали.
Разделители между думите са интервали или край на ред (white
space).
// words.cpp
#include <iostream> #include <string> using namespace std; int main() { int count = 0; string word; while (cin >> word) count++; cout << count << " words." << endl; return 0; }
nkirov@cpp % c++ words.cpp nkirov@cpp % ./a.out abc xyz 123 words stop 5 words. nkirov@cpp % |
string line;Пример: Брой на редовете от входния поток.
while (getline(cin, line))
{ process line
}
nkirov@cpp % c++ lines.cpp nkirov@cpp % ./a.out first line secod line one empty line 4 lines. |
char ch;Пример: Брой на символите (знаците) във входния поток.
while (cin.get(ch))
{ process ch
}
nkirov@cpp % c++ chars.cpp nkirov@cpp % ./a.out 12345 aaaaa 12 chars. nkirov@cpp % |
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
#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; }
nkirov@cpp % c++ dice.cpp nkirov@cpp % ./a.out 2 1 1 6 2 3 5 1 nkirov@cpp % ./a.out 5 3 3 2 3 1 5 1 |
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
/* 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;
}
nkirov@cpp % c++ sqrtest4.cpp nkirov@cpp % ./a.out Test passed. squareroot of 7.82637 = 2.79756 Test passed. squareroot of 131538 = 362.681 Test passed. squareroot of 755605 = 869.256 Test passed. squareroot of 458650 = 677.237 |
nkirov@cpp % c++ sqrtest5.cpp nkirov@cpp % ./a.out Test passed. squareroot of 300597 = 548.267 Test passed. squareroot of 135541 = 368.159 Test passed. squareroot of 41061.9 = 202.637 Test passed. squareroot of 127746 = 357.416 |
Функциите често съдържат
неявни предположения - напр. знаменатели трябва да са различни от
нула, заплатите не трябва да бъдат отрицателни и т.н.
Такива "незаконни" стойности могат "да се вмъкнат" в програмата от
входа или в резултат на предишна грешка.
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;
nkirov@cpp % c++ futval0.cpp nkirov@cpp % ./a.out Please enter the interest rate in percent: 10 After 10 years, the balance is 2593.74 nkirov@cpp % ./a.out Please enter the interest rate in percent: -10 Assertion failed: (p > 0), function future_value, file futval0.cpp, line 9. zsh: abort ./a.out nkirov@cpp % |
string int_name(int n)[trace.cpp]
{ cout << "Entering digit_name. n = " << n << "\n";
...
cout << "Exiting digit name. Return value = "
<< s << "\n";
return s;
}