Сделал контейнер для ключей типа "double" без чёткого совпадения ключа. Интересно было бы получить конструктивной критики
- Войдите на сайт для отправки комментариев
Возникла задача, для которой понадобилось хранить данные в таком виде (почти буквально) :
array.set(0.0, 0); array.set(1.0, 1); array.set(2.0, 2); array.set(3.0, 3);
И чтобы при этом такой код :
array.get(0.3)
Вернул 0 - т.е. содержимое по ближайшему к 0.3 ключу.
Готовых контейнеров такого типа не нашёл, потому написал свой. Также, т.к. fpu нет, а значений которые требуется различить немного - сделал сведение "ключа" к целому виду (с домножением на коофицент - т.е. допустим 0*50 = 0, 1*50 = 50, ... 3*50=150, 0.3*50 = 15 - ближе к 0, и притом ключ можно уложить в 1 байт). Можно и сводить к double с коофицентом 1, но в моей задаче не трубуется. Однако устройство позволяет.
Ну и до кучи - загрузка/сохранение в EEPROM, возможно есть более простые методы?
Код тут или на гитхабе
#ifndef FloatKeyContainer_H #define FloatKeyContainer_H #include <EEPROM.h> typedef unsigned int size_t; typedef uint8_t byte; /* * size - максимальный размер * key_trim_cooficient - коофицент для приведения ключа к key_type (например, для ключа между 0.0 и 1.0, и типа ключа "байт" - 255) * key_type - тип ключа * value_type - тип хранимого значения */ template<size_t size, int key_trim_cooficient, typename key_type, typename value_type> class FloatKeyContainer { private: size_t elements; key_type keys[size]; value_type values[size]; public: /* * Создадим и зальём нулями */ FloatKeyContainer() { for(size_t i=0;i<size;i++) { this->keys[i] = 0.0; this->values[i] = 0; } this->elements = 0; } /* * Приведем от double к нашему типу */ key_type key_converted(double key) { return (key_type)(key*key_trim_cooficient); } /* * Вставка пары "ключ-значение" */ void insert(double dkey, value_type value) { key_type key = this->key_converted(dkey); if(this->elements==0) { this->keys[0] = key; this->values[0] = value; this->elements = 1; } else { //Т.к. используется бинарный поиск ключа - массив ключей должен быть отсортирован. И значения должны располагаться в соответсвующих ключу позициях size_t i; for(i=0;i<this->elements;i++) { //Ищем позицию для вставки if(this->keys[i]>=key) { break; } } for(size_t j=i; j<size-1; j++) { //Сдвигаем элементы вправо this->keys[j+1] = this->keys[j]; this->values[j+1] = this->values[j]; } //Добавляем пару this->elements++; this->keys[i] = key; this->values[i] = value; } } /* * Получение номера "ближайшего" ключа (сравнением с 2 существующими) * key - результат приведения ключа от double к внутреннему типу * first - идекс меньшего ключа из this->keys * second - индекс большего из this->keys */ inline size_t getNearestKey(key_type key, size_t first, size_t second) { return abs(key-this->keys[first]) < abs(this->keys[second]-key) ? first : second; } /* * Номер ближайшего ключа (во всем массиве this->keys) * dkey - ключ для сравнения (в непреобразованном виде) */ size_t getIndex(double dkey) { key_type key = this->key_converted(dkey); if(this->elements==1) { return 0; } else { //Бинарный поиск size_t left = 0, right = this->elements, middle; while(left<=right) { middle = left+(right-left)/2; if(key<this->keys[middle]) { right = middle-1; } else if(key>this->keys[middle]) { left = middle+1; } else { break; } } //Сравнение исходного числа, найденного ключа и 2 соседних (если они есть) if( (middle!=0 && middle!=elements-1) && this->elements>2) { return this->getNearestKey(key, this->getNearestKey(key, middle-1, middle), this->getNearestKey(key, middle, middle+1) ); } else if(middle==0) { if(elements<2) { return 0; } else { return this->getNearestKey(key, 0, 1); } } else if(middle==elements-1) { if(size<2) { return 0; } else { return this->getNearestKey(key, 0, 1); } } } } /* * Получение значения по ключу */ value_type get(double key) { return this->values[this->getIndex(key)]; } /* * Задание значения по ключу */ void set(double key, value_type value) { this->values[this->getIndex(key)] = value; } /* * Загрузка из EEPROM */ void load(size_t address) { //Чтение числа элементов byte* elements_count_memory = (byte*)&this->elements; size_t elements_count_memory_size = sizeof(size_t); for(size_t i=0; i<elements_count_memory_size; i++) { elements_count_memory[i] = EEPROM.read(address + i); } //Ключи byte* keys_memory = (byte*)this->keys; size_t keys_memory_size = sizeof(key_type)*size; for(size_t i=0; i<keys_memory_size; i++) { keys_memory[i] = EEPROM.read(address + elements_count_memory_size + i); } //Значения byte* values_memory = (byte*)this->values; size_t values_memory_size = sizeof(value_type)*size; for(size_t i=0; i<values_memory_size; i++) { values_memory[i] = EEPROM.read(address + elements_count_memory_size + keys_memory_size + i); } } /* * Загрузка в EEPROM */ void save(size_t address) { //Сохранение числа элементов byte* elements_count_memory = (byte*)&this->elements; size_t elements_count_memory_size = sizeof(size_t); for(size_t i=0; i<elements_count_memory_size; i++) { EEPROM.write(address + i, elements_count_memory[i]); } //Сохранение ключей byte* keys_memory = (byte*)this->keys; size_t keys_memory_size = sizeof(key_type)*size; for(size_t i=0; i<keys_memory_size; i++) { EEPROM.write(address + elements_count_memory_size + i, keys_memory[i]); } //Сохранение значений byte* values_memory = (byte*)this->values; size_t values_memory_size = sizeof(value_type)*size; for(size_t i=0; i<values_memory_size; i++) { EEPROM.write(address + elements_count_memory_size + keys_memory_size + i, values_memory[i]); } } }; #endif
Вот пример
FloatKeyContainer<4, 50, byte, int> dict; dict.insert(0.0, 0); dict.insert(1.0, 1); dict.insert(2.0, 2); dict.insert(3.0, 3); dict.save(2); ... FloatKeyContainer<4, 50, byte, int> other; other.load(2); Debug("Found next : ", other.get(1.3));
Выдает, как и следовал ждать - 1.
Может кто-то предложит, что можно здесь улучшить?
Скорее всего специальное применение. getNearest можно вынести как функтор или параметр шаблона, тогда можно искать ближайший меньший или больший.
Ещё, бинарный поиск кажется слегка сложная реализация, но я щас уже сплю, потому могу ошибаться.
Угадали, да. Но есть на примете пара идей :-)
Возможно, есть смысл. Но по моему целесообазнее сделать её переопределяемой - чтобы можно было создать класс-потомок. Может накидаю завтра обновленный вариант.
По моему тут больше заняло сравнение не только с найденным ключом, но и 2 соседними (ну нашло оно 1, допустим. Неплохо бы с 0 и 2 сравнить же :-) )