Отображения и словари

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

Открытая адресация

Открытая адресация используется для разрешения коллизий. Коллизии — очень частое явление, возникающее, когда два элемента хешируются в один и тот же слот или местоположение. Например, при такой настройке хеша:

../_images/collision.png

Если поступает запрос на заполнение слота 0 значением 70, то, поскольку слот 0 не пуст, открытая адресация находит следующий доступный слот в словаре для обслуживания этого запроса. Такой последовательный поиск альтернативного местоположения называется пробированием. Существует несколько алгоритмов последовательного пробирования, но MicroPython использует линейное пробирование, которое описано в следующем разделе.

Линейное пробирование

Линейное пробирование — один из методов поиска доступного адреса или слота в словаре. В MicroPython оно используется вместе с открытой адресацией. Для обслуживания описанного выше запроса, в отличие от других алгоритмов пробирования, линейное пробирование предполагает фиксированный интервал 1 между пробами. Поэтому запрос будет обслужен путём помещения элемента в следующий свободный слот, которым в нашем примере является слот 4:

../_images/linprob.png

Те же методы, т. е. открытая адресация и линейное пробирование, используются для поиска элемента в словаре. Предположим, мы хотим найти элемент данных 33. Вычисленное значение хеша будет равно 2. Обращение к слоту 2 показывает 33. На этом этапе мы возвращаем True. Поиск 70 существенно отличается, так как при вставке произошла коллизия. Поэтому вычисленное значение хеша равно 0, в котором сейчас находится 44. Вместо того чтобы просто вернуть False, мы выполняем последовательный поиск, начиная с точки 1, пока не найдём элемент 70 или не встретим свободный слот. Это общий способ выполнения поиска в хешах:

// not yet found, keep searching in this table
pos = (pos + 1) % set->alloc;

if (pos == start_pos) {
    // search got back to starting position, so index is not in table
    if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
        if (avail_slot != NULL) {
            // there was an available slot, so use that
            set->used++;
            *avail_slot = index;
            return index;
        } else {
            // not enough room in table, rehash it
            mp_set_rehash(set);
            // restart the search for the new element
            start_pos = pos = hash % set->alloc;
        }
    }
} else {
     return MP_OBJ_NULL;
}