Информатика и технология программирования

         

Смысл переменных


Первое, что следует усвоить - любое действие (операция, оператор, фрагмент программы) можно проектировать, если известны переменные, над которыми это действие осуществляется. С другой стороны, действие, выполненное над переменной, определяет результат, который в ней будет содержаться, а следовательно и ее смысл. Переменные, таким образом, вводятся в программу не просто так, а по " настоятельной необходимости" :



- сначала формулируется результат, чего, собственно, мы хотим достигнуть ;



-затем определяется, какие для этого потребуются переменные, и какой " смысл" они будут иметь :



-в конце выбирается стандартный фрагмент алгоритма, который придает переменной этот смысл.

Перечислим наиболее простые варианты смысла переменных в программе:

l ПЕРЕМЕННАЯ - СЧЕТЧИК. Переменная считает количество появлений в программе того или иного события. В следующем примере переменная m подсчитывает количество положительных переменных в массиве. Заметим, что ключевой "фразой", определяющей смысл переменной-счетчика, является следующая :



for (...) { if (...удовлетворяет условию...) m++; }


for (i=0, m=0; i&#60n; i++) if(A[i]&#62 0) m++;

l ПЕРЕМЕННАЯ - ПРИЗНАК. Переменная фиксирует факт наступления какого-либо события в программе. Такая переменная принимает только логические значения 0 или 1 (событие наступило или не наступило). В одной точке программы проверяется это условие и устанавливается признак, в другой - наличие или отсутствие признака оказывает влияние на логику работы программы, в третьей - признак сбрасывается. Простой пример - суммирование элементов массива до первого отрицательного включительно:


for (s=0, k=0, i=0; i&#60n &#38&#38 k==0; i++)
{
s+=A[i];
if (A[i]&#60 0) k=1;
}

В данном случае признак обнаружения отрицательного элемента в массиве прекращает выполнение цикла. В таких случаях использование break позволяет обойтись без такого признака.


for (s=0, i=0; i&#60n; i++)
{
s+=A[i];
if (A[i]&#60 0) break;




}

Более сложный пример иллюстрирует тот факт, что переменная-признак " запоминает" факт наступления события и сохраняет его в течение некоторого времени.



for (i=0,s=0,k=0; i&#60 10; i++)
if (A[i]&#60 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]== * &#38&#38 src[i+1]== / )
{ com=0; i++; } // не в комментарии, пропустить символ

}
else
{ // вне комментария

if (src[i]== / &#38&#38 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&#60 10; i++) s=s+A[i];

То же самое можно сказать и о произведении, которое накапливается следующим фрагментом :




for (s=1,...;...;...;) { Получить k; s=s*k; }

Заметим, что смысл переменной не меняется в зависимости от того, какими конструкциями окружен сам фрагмент. В приведенных них примерах накапливается сумма значений, полученных разными способами и от разных источников :

for (s=0,i=0; i&#60n; i++) // Сумма элементов массива

s+=A[i];

for (s=0,i=0; i&#60n &#38&#38 A[i]&#62=0; i++) // Сумма элементов массива до первого

s+=A[i]; // отрицательного

for (s=0,i=0; i&#60n; i++) // Сумма положительный элементов

if (A[i]&#62 0) s+=A[i]; // массива

for (s=0,x=0; x&#60=1; x+=0.1) // Сумма значений функции sin

s+=sin(x); // в диапазоне 0..1 с шагом 0.1

for (s=0,...;...;...) { получить k; if (k&#62s) s=k; }





Для доказательства этого факта можно привлечь метод математической индукции : действительно, если на очередном шаге s содержит максимальное значение, полученное на предыдущих шагах, то после выполнения if (k&#62s) s=k; она будет содержать такой же максимум, но уже с учетом текущего шага. Типичный пример - нахождение максимального элемента массива.

for (s=0,i=0; i&#60 10; i++) if (A[i]&#62s) s=A[i];

Рассмотрим более сложные вариации на эту тему. Следующий фрагмент запоминает не само значение максимума, а номер элемента в массиве, где оно находится .

for (i=1,k=0; i&#60 10; i++) if (A[i]&#62A[k]) k=i;

И, наконец, если в просматриваемой последовательности в поиске максимума /минимума используются не все элементы, а ограниченные дополнительным условием (например, минимальный из положительных), в программе должен быть учтен факт того, что она начинает работу при отсутствии элемента, выбранного в качестве максимального /минимального

for (i=0,k=-1; i&#60 10; i++) // k=-1 - нет элемента, принятого за минимальный

{ if (A[i]&#60 0) continue;
if (k==-1 || A[i]&#60A[k]) k=i;
}


Содержание раздела