Страница 78 из 93
71. Немедленно обрабатывайте особые случаи.
Пишите свои алгоритмы таким образом, чтобы они обрабатывали вырожденные случаи немедленно. Вот тривиальный пример того, как сделать это неправильно:
print( const char *str ){
if ( !*str ) // ничего не делать, строка пуста return;while ( *str ) putchar( *str++ );}Оператор if тут не нужен, потому что этот случай хорошо обрабатывается циклом while.Листинги 2 и 3 демонстрируют более реалистический сценарий. Листинг 2 определяет умышленно наивный заголовок связанного списка и функцию для удаления из него элемента.
Листинг 2. Связанный список: Вариант 1.
- typedef struct node
- {
- struct node *next, *prev;
- //...
- } node;
- node *head;
- remove( node **headp, node *remove )
- {
- // Удалить элемент, на который указывает "remove", из списка, на начало
- // которого указывает *headp.
- if ( *headp == remove ) // Этот элемент в начале списка.
- {
- if ( remove->next ) // Если это не единственный элемент в списке,
- remove->next->prev = NULL; // то поместите следующий за ним элемент
- // первым в списке.
- *headp = remove->next;
- }
- else // Элемент находится в середине списка
- {
- remove->prev->next = remove->next;
- if ( remove->next )
- remove->next->prev = remove->prev;
- }
- }
Смысл этого состоит в том, что незначительная переделка этой задачи позволяет мне использовать алгоритм, не имеющий особых случаев, этим самым упрощая программу. Конечно, эта простота не дается бесплатно - теперь стало невозможным перемещение по списку в обратном направлении - но нам ведь это может быть и не нужно.
Листинг 3. Связанный список: Вариант 2.
- typedef struct node
- {
- struct node *next, **prev; // ?=== К prev добавлен символ *
- // ...
- } node;
- node *head;
- remove( node **headp, node *remove )
- {
- if ( *(remove->prev) = remove->next ) // если не в конце списка,
- remove->next->prev = remove->prev; // то уточнить следующий элемент
- }