3.1.3 Таблица имен

К таблице имен доступ осуществляется с помощью одной функции

name* look(char* p, int ins =0);

Ее второй параметр указывает, нужно ли сначала поместить строку символов в таблицу. Инициализатор =0 задает параметр, который надлежит использовать по умолчанию, когда look() взывается с одним параметром. Это дает удобство записи, когда look(«sqrt2») означает look(«sqrt2»,0), то есть просмотр, без помещения в таблицу. Чтобы получить такое же удобство записи для помещения в таблицу, определяется вторая функция:

inline name* insert(char* s) (* return look(s,1);*)

Как уже отмечалось раньше, элементы этой таблицы имеют тип:

srtuct name (* char* string; char* next; double value; *)

Член next используется только для сцепления вместе имен в таблице.

Сама таблица – это просто вектор указателей на объекты типа name:

const TBLSZ = 23; name* table[TBLSZ];

Поскольку все статические объекты инициализируются нлем, это тривиальное описание таблицы table гарантирует также надлежащую инициализацию.

Для нахождения элемента в таблице в look() принимается простой алгоритм хэширования (имена с одним и тем же хэш-кдом зацепляются вместе):

int ii = 0; // хэширование char* pp = p; while (*pp) ii = ii««1 ^ *pp++; if (ii « 0) ii = -ii; ii %= TBLSZ;

То есть, с помощью исключающего ИЛИ каждый символ во входной строке «добавляется» к ii («сумме» предыдущих символов). Бит в x^y устанавливается единичным тогда и только тода, когда соответствующие биты в x и y различны. Перед примнением в символе исключающего ИЛИ, ii сдвигается на один бит влево, чтобы не использовать в слове только один байт. Это можно было написать и так:

ii ««= 1; ii ^= *pp++;

Кстати, применение ^ лучше и быстрее, чем +. Сдвиг важен для получения приемлемого хэш-кода в обоих случаях. Операторы

if (ii « 0) ii = -ii; ii %= TBLSZ;


обеспечивают, что ii будет лежать в диапазоне 0...TBLS1; % – это операция взятия по модулю (еще называемая получнием остатка).

Вот функция полностью:

extern int strlen(const char*); extern int strcmp(const char*, const char*); extern int strcpy(const char*, const char*);

name* look(char* p, int ins =0) (* int ii = 0; // хэширование char* pp = p; while (*pp) ii = ii««1 ^ *pp++; if (ii « 0) ii = -ii; ii %= TBLSZ;

for (name* n=table[ii]; n; n=n-»next) // поиск if (strcmp(p,n-»string) == 0) return n;

if (ins == 0) error(«имя не найдено»);

name* nn = new name; // вставка nn-»string = new char[strlen(p)+1]; strcpy(nn-»string,p); nn-»value = 1; nn-»next = table[ii]; table[ii] = nn; return nn; *)

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

Добавление нового name включает в себя создание нового объекта в свободной памяти с помощью операции new (см. #3.2.6), его инициализацию, и добавление его к списку имен. Последнее осуществляется просто путем помещения нового имени в голову списка, поскольку это можно делать даже не проверяя, имеется список, или нет. Символьную строку для имени тоже нужно сохранить в свободной памяти. Функция strlen() исползуется для определения того, сколько памяти нужно, new – для выделения этой памяти, и strcpy() – для копирования строки в память.