Смысл переменных
Первое, что следует усвоить - любое действие (операция, оператор, фрагмент программы) можно проектировать, если известны переменные, над которыми это действие осуществляется. С другой стороны, действие, выполненное над переменной, определяет результат, который в ней будет содержаться, а следовательно и ее смысл. Переменные, таким образом, вводятся в программу не просто так, а по " настоятельной необходимости" :
- сначала формулируется результат, чего, собственно, мы хотим достигнуть ;
-затем определяется, какие для этого потребуются переменные, и какой " смысл" они будут иметь :
-в конце выбирается стандартный фрагмент алгоритма, который придает переменной этот смысл.
Перечислим наиболее простые варианты смысла переменных в программе:
l ПЕРЕМЕННАЯ - СЧЕТЧИК. Переменная считает количество появлений в программе того или иного события. В следующем примере переменная m подсчитывает количество положительных переменных в массиве. Заметим, что ключевой "фразой", определяющей смысл переменной-счетчика, является следующая :
for (...) { if (...удовлетворяет условию...) m++; }
for (i=0, m=0; i<n; i++) if(A[i]> 0) m++;
l ПЕРЕМЕННАЯ - ПРИЗНАК. Переменная фиксирует факт наступления какого-либо события в программе. Такая переменная принимает только логические значения 0 или 1 (событие наступило или не наступило). В одной точке программы проверяется это условие и устанавливается признак, в другой - наличие или отсутствие признака оказывает влияние на логику работы программы, в третьей - признак сбрасывается. Простой пример - суммирование элементов массива до первого отрицательного включительно:
for (s=0, k=0, i=0; i<n && k==0; i++)
{
s+=A[i];
if (A[i]< 0) k=1;
}
В данном случае признак обнаружения отрицательного элемента в массиве прекращает выполнение цикла. В таких случаях использование break позволяет обойтись без такого признака.
for (s=0, i=0; i<n; i++)
{
s+=A[i];
if (A[i]< 0) break;
}
Более сложный пример иллюстрирует тот факт, что переменная-признак " запоминает" факт наступления события и сохраняет его в течение некоторого времени.
for (i=0,s=0,k=0; i< 10; i++)
if (A[i]< 0) k=1;
else
{ if (k==1) s++; k=0; }
Несложно догадаться, что " смысл" переменной k - текущий элемент массива является отрицательным, а " смысл" s - счетчик. Счетчик увеличивается, если выполняется ветка else - текущий элемент массива положителен, и в то же самое время k==1 - соответствует отрицательному значению элемента массива. Преодолеть это противоречие можно, если учесть, что признак проверяется в этой ветке до его изменения, то есть l проверяется его значение, оставшееся от предыдущего шага. Следовательно. фрагмент подсчитывает количество пар элементов вида " отрицательный - положительный" .
Еще один пример - обнаружение комментариев в строке. Признак com в процессе переписывания строки устанавливается в 1, если программа находится " внутри комментария" .
void copy(char dst[], char src[])
{ int i,com=0,j=0;
for (com=0,i=0; src[i]!=0; i++)
if (com)
{ // внутри комментария
if (src[i]== * && src[i+1]== / )
{ com=0; i++; } // не в комментарии, пропустить символ
}
else
{ // вне комментария
if (src[i]== / && src[i+1]== * )
{ com=1; i++; } // в комментарии, пропустить символ
else
dst[j++] = src[i]; // переписать символ в выходную строку
}
dst[j]=0; }
for (s=0,...;...;...) { получить k; s=s+k; }
Он дает переменной s единственный " смысл" - переменная накапливает сумму значений k , полученных на каждом из шагов выполнения цикла. Для доказательства этого факта можно привлечь метод математической индукции : действительно, если на очередном шаге s содержит сумму, накопленную на предыдущих шагах, то после выполнения s=s+k она будет содержать сумму уже с учетом текущего шага. Типичный пример - сумма элементов массива.
for (s=0,i=0; i< 10; i++) s=s+A[i];
То же самое можно сказать и о произведении, которое накапливается следующим фрагментом :
for (s=1,...;...;...;) { Получить k; s=s*k; }
Заметим, что смысл переменной не меняется в зависимости от того, какими конструкциями окружен сам фрагмент. В приведенных них примерах накапливается сумма значений, полученных разными способами и от разных источников :
for (s=0,i=0; i<n; i++) // Сумма элементов массива
s+=A[i];
for (s=0,i=0; i<n && A[i]>=0; i++) // Сумма элементов массива до первого
s+=A[i]; // отрицательного
for (s=0,i=0; i<n; i++) // Сумма положительный элементов
if (A[i]> 0) s+=A[i]; // массива
for (s=0,x=0; x<=1; x+=0.1) // Сумма значений функции sin
s+=sin(x); // в диапазоне 0..1 с шагом 0.1
for (s=0,...;...;...) { получить k; if (k>s) s=k; }
Для доказательства этого факта можно привлечь метод математической индукции : действительно, если на очередном шаге s содержит максимальное значение, полученное на предыдущих шагах, то после выполнения if (k>s) s=k; она будет содержать такой же максимум, но уже с учетом текущего шага. Типичный пример - нахождение максимального элемента массива.
for (s=0,i=0; i< 10; i++) if (A[i]>s) s=A[i];
Рассмотрим более сложные вариации на эту тему. Следующий фрагмент запоминает не само значение максимума, а номер элемента в массиве, где оно находится .
for (i=1,k=0; i< 10; i++) if (A[i]>A[k]) k=i;
И, наконец, если в просматриваемой последовательности в поиске максимума /минимума используются не все элементы, а ограниченные дополнительным условием (например, минимальный из положительных), в программе должен быть учтен факт того, что она начинает работу при отсутствии элемента, выбранного в качестве максимального /минимального
for (i=0,k=-1; i< 10; i++) // k=-1 - нет элемента, принятого за минимальный
{ if (A[i]< 0) continue;
if (k==-1 || A[i]<A[k]) k=i;
}