Работа с длинами и позициями
В программировании мы часто имеем понятие как об относительной «длине» чего-либо, так и о его абсолютном «положении» в некотором окружении. Когда мы сравниваем два элемента, они должны быть либо относительными, либо абсолютными.
Вот несколько примеров из двух:
Домен | Родственник | Абсолют |
---|---|---|
время | продолжительность | свидание |
указатели | индекс | указатель |
массивы | длина | должность |
(чтобы убедиться, что указатели и массивы тесно связаны)
Относительный элемент может быть преобразован в абсолют путем сложения, и для этого требуется абсолют, заданный в качестве базовой линии:
absolute2 = absolute1 + relative; // or relative + absolute
Продолжительность можно преобразовать в дату, добавив базовую дату.
date = 1 /*day*/ + Today; // Tomorrow..
Индекс можно преобразовать в указатель, добавив базовый указатель.
char *p ...;
int i ...;
char *q = p + i; // or i + p;
Длину можно преобразовать в позицию, добавив некоторую базовую позицию.
int startPosition ... ;
int length ...;
int onePastTheEndPosition = startPosition + length; // or length + startPosition;
Абсолютные элементы можно преобразовать в относительные путем вычитания, и для этого требуется два абсолютных элемента:
int startPosition ... ;
int endPosition ... ;
int length = endPostion - startPosition;
Теперь заметим, что, как и в случае массивов с отсчетом от нуля, результатом добавления базового абсолютного значения к длине является другое абсолютное значение. это один после конца элемента длины. Таким образом, возможна ошибка на одну ошибку, которой обычно избегают, рассматривая диапазон (startPos, length; также startPos, endPos) как включающий первую позицию и исключающий конечную позицию.
Давайте посмотрим на этот цикл, где управляющая переменная цикла проходит длину некоторого элемента, который должен быть расположен где-то внутри массива. (Такой цикл может искать слово в строке, помещая его во внешний цикл, который продвигается вперед. startPosition
):
for ( i = 0; i < length; i++ )
a [ startPosition + i ] ...
Потому что a[0]
относится к абсолютному первому элементу, массивы должны быть проиндексированы по абсолютной позиции, здесь мы конвертируем i
от элемента относительной длины до абсолютной позиции в массиве для индексации.
В качестве альтернативы мы можем вычислить положение непосредственно в переменной управления циклом.
for ( i = startPosition; i < startPosition + length; i++ )
a [ i ] ...
Здесь i
— это позиция с уже добавленной базовой позицией. Добавляя базовую линию во время инициализации цикла, мы также должны использовать абсолютную позицию во время условного управления циклом. (Как и в предыдущем случае, этот цикл можно использовать для поиска слова в строке, заключая его во внешний цикл, продвигающий startPosition.)
Когда мы пишем простые циклы for:
for ( int i = 0; i < N; i++ ) ...
Похоже, что мы не можем на самом деле сказать, является ли переменная управления циклом длиной или позицией, но на самом деле это и то, и другое, потому что, если бы мы использовали здесь базовую абсолютную позицию, она была бы равна нулю. Таким образом, поскольку 0+0 равно нулю, а N+0 равно N, эта управляющая переменная цикла обладает свойствами как относительной длины, так и абсолютного положения.
Вот более полный код, где внешний цикл проверяет каждую позицию, чтобы увидеть, находит ли там определенное слово внутренний цикл:
char *textToSearch = "hello world";
char *toMatch = "world";
int N = strlen(textToSearch);
int M = strlen(toMatch);
for ( int startPosition = 0; startPosition < N; startPosition++ ) {
int i; // declared outside the for so we can use it after
for ( i = 0; i < M; i++ ) {
if ( textToSearch [ startPosition + i ] != toMatch [ i ] )
break; // stop the inner loop and move on to outer loop
}
if ( i == M ) {
printf ("match found at %d\n", startPosition );
// we might stop the outer loop if we're only looking for the first ocurrence
} else {
printf ("match not found at %d\n", startPosition );
}
}
Заметим, что управляющая переменная внутреннего цикла, i
является позицией в двух случаях: textToSearch [ startPosition + i ]
идентифицирует символ в следующей позиции, чтобы начать поиск в большей строке, в то время как toMatch [ i ]
идентифицирует символ в следующей позиции в слове для сравнения.