diff --git a/README.md b/README.md
index 06197e1..c1415c3 100644
--- a/README.md
+++ b/README.md
@@ -1,372 +1,34 @@
-# Persistent-Data-Structures
+# Undo-Redo
-Курсовой проект по дисциплине "Современные методы программирования" - "Persistent data structures"
-
-## Разработчики:
-* *Карицкая Полина, 24225.1*
-* *Пучков Дмитрий, 24225.1*
+Реализация дополнительного требования курсовой работы по дисциплине "Современные методы программирования" - "Persistent data structures"
---
## Оглавление
-- [Persistent-Data-Structures](#persistent-data-structures)
- - [Разработчики:](#разработчики)
- - [Оглавление](#оглавление)
- - [Описание задания](#описание-задания)
- - [Базовые требования](#базовые-требования)
- - [Дополнительные требования](#дополнительные-требования)
- - [Календарный план](#календарный-план)
- - [Ожидаемое решение](#ожидаемое-решение)
- - [Теоретическая часть](#теоретическая-часть)
- - [Персистентные структуры данных](#персистентные-структуры-данных)
- - [Fat node](#fat-node)
- - [Path copying](#path-copying)
- - [Более эффективное по скорости доступа представление структур данных](#более-эффективное-по-скорости-доступа-представление-структур-данных)
- - [API](#api)
- - [Примеры использования](#примеры-использования)
- - [Массив (Persistent Array)](#массив-persistent-array)
- - [Список (Persistent Linked List)](#список-persistent-linked-list)
- - [Aссоциативный массив (Persistent Map)](#aссоциативный-массив-persistent-map)
- - [Используемые источники](#используемые-источники)
-
----
-## Описание задания
-
-Реализовать библиотеку в Python со структурами данных в persistent-вариантах.
-
----
-## Базовые требования
-- [x] Массив (константное время доступа, переменная длина)
-- [x] Двусвязный список
-- [x] Ассоциативный массив (на основе Hash-таблицы, либо бинарного дерева)
-
----
-## Дополнительные требования
-- [x] Реализовать более эффективное по памяти представление структур данных;
-- [x] Реализовать универсальный undo-redo механизм для перечисленных структур с поддержкой каскадности (для вложенных структур);
-- [x] Реализовать поддержку транзакционной памяти (STM).
-
----
-## Календарный план
-
-| **Сроки** | **Этап работы** | **Разделение ответственностей** |
-|--------------------|----------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
-| **до 23.11.2024** | - Создание каркаса проекта
- Создание репозитория на GitHub | - Ответственный за настройку проекта и репозитория: *Полина*
- Ответственный за организацию структуры файлов и начальную настройку CI/CD: *Дмитрий* |
-| **до 07.12.2024** | **Реализация базовой функциональности:**
- Массив (Persistent Array)
- Двусвязный список (Persistent Linked List)
- Ассоциативный массив (Persistent Map)
- Создание единого API | - Ответственный за реализацию Persistent Array: *совместно*
- Ответственный за реализацию Persistent Linked List: *Полина*
- Ответственный за реализацию Persistent Map: *Дмитрий*
- Ответственный за создание API: *совместно* |
-| **до 21.12.2024** | **Реализация дополнительной функциональности:**
- Реализовать более эффективное по памяти представление структур данных
- Реализовать универсальный undo-redo механизм для перечисленных структур с поддержкой каскадности (для вложенных структур)
- Реализовать поддержку транзакционной памяти (STM) | - Ответственный за улучшение эффективности по памяти: *Полина*
- Ответственный за реализацию undo-redo механизма: *Дмитрий*
- Ответственный за поддержку транзакционной памяти (STM): *совместно* |
+- [Механизм Undo-Redo](#механизм-undo-redo)
+- [Механизм Undo-Redo с использованием персистентных структур данных](#механизм-undo-redo-с-использованием-персистентных-структур-данных)
---
-## Ожидаемое решение
-
-Ожидаемое решение состоит в разработке библиотеки, которая будет поддерживать следующие структуры данных:
-
-1. **Persistent Array** - массив с возможностью добавления элементов без изменения предыдущих состояний. Операции должны поддерживать константное время доступа.
-
-2. **Persistent Linked List** - двусвязный список с поддержкой добавления и удаления элементов, при этом сохраняется возможность обратиться к предыдущим состояниям списка.
-
-3. **Persistent Map** - ассоциативный массив, реализованный на основе хеш-таблицы или бинарного дерева поиска. Это будет позволять эффективно работать с данными, при этом поддерживать возможность обращения к предыдущим версиям данных.
-
-4. **Оптимизация по памяти** - предложим более эффективное по памяти представление данных операций доступа по сравнению с fat-node.
+## Механизм Undo-Redo
-5. **Undo/Redo Механизм** - реализуем механизм для всех структур данных, который позволит отменять и повторять изменения, причем для вложенных структур будет поддерживаться каскадность.
-
-6. **Транзакционная память (STM)** - реализуем механизм транзакционной памяти для обеспечения атомарности операций с данными.
-
-
-Каждая из структур данных будет поддерживать все основные операции: вставку, удаление, обновление, а также предоставлять возможность работы с предыдущими версиями данных через персистентность данных. Вся библиотека будет иметь единый API для работы с данными и их модификацией. Также будет обеспечена возможность взаимодействия с данными в виде вложенных структур.
+**Механизм undo-redo** — механизм, позволяющий пользователям отменять или повторно применять изменения. Он обеспечивает гибкость и возможность восстановления ошибок, что особенно важно для задач, требующих частого редактирования. В ветке рассматриваются реализация механизма undo-redo с использованием персистентных структур данных.
---
-## Теоретическая часть
-
-### Персистентные структуры данных
-
-**Персистентные структуры данных** сохраняют предыдущие версии при изменении. Структура называется *fully persistent*, если все её версии доступны для изменений. В *partially persistent* структурах можно изменять только последнюю версию, но доступ к предыдущим возможен. Эти структуры часто реализуются с использованием алгоритмов, таких как path copying, node copying и fat node, а также с применением бинарных деревьев поиска и красно-черных деревьев.
-
-Персистентные структуры используются в вычислительной геометрии, объектно-ориентированном программировании, текстовых редакторах, симуляциях и системах контроля версий, таких как Git.
-
-### Fat node
-
-*Fat node* используется для создания персистентных структур данных, где изменения сохраняются только в измененных узлах дерева, а сами узлы могут расширяться, чтобы хранить все версии. Основной принцип заключается в том, чтобы хранить обновления и версии данных в списках, что позволяет эффективно отслеживать изменения в структуре.
-
-**Преимущества:**
-
-* Экономия памяти, так как изменяются только те части данных, которые действительно изменились;
-* Быстрая работа с историей изменений.
-
-**Недостатки:**
-
-* Может быть менее эффективен для структур с частыми изменениями, так как увеличение размера узлов приводит к дополнительным затратам на память.
-
-Можно посмотреть [визуализацию метода *fat node*](https://kumom.io/persistent-bst).
-
-### Path copying
-
-Метод *path copying* используется для создания персистентных структур данных, где при изменении узла создается его копия, и нужно пройти по всем узлам от измененного до корня, чтобы обновить ссылки на новый узел. Этот метод упрощает доступ к данным, так как для поиска нужной версии достаточно пройти путь от корня.
-
-**Преимущества:**
-
-* Быстрый доступ к данным;
-* Простота в реализации.
-
-**Недостатки:**
-
-* Большие затраты на память, поскольку изменения могут потребовать копирования всей структуры;
-* Могут возникать дополнительные накладные расходы при частых модификациях.
-
-Метод *path copying* также используется для создания полностью персистентных структур данных.
-
-На иллюстрации продеминстрирован пример использования *path copying* на бинарном дереве поиска. При внесении изменения создается новый корень, при этом старый корень сохраняется для дальнейшего использования (он показан темно-серым цветом). Заметим также, что старое и новое деревья частично делят общую структуру.
-
-
-
-### Более эффективное по памяти представление структур данных
-
-Поскольку одно из требований — использование более эффективного подхода по сравнению с методом *fat-node*, в проекте будет применяться подход с использованием B-деревьев.
+## Механизм Undo-Redo с использованием персистентных структур данных
----
-## API
-
-Для работы с персистентными структурами данных используйте следующий API:
-
-Создание объекта:
-```python
-from persistent_data_structure import PersistentLinkedList, PersistentArray, PersistentMap
-
-lst = PersistentLinkedList()
-arr = PersistentLinkedList()
-dct = PersistentMap()
-```
-
-Обращение к элементу текщей версии:
-```python
-lst[index]
-arr[index]
-dct[key]
-```
-
-Обращение по индексу к элементу произвольной версии для массива и списка:
-```python
-arr.get(version, index)
-lst.get(version, index)
-```
-
-Обращение по ключу к элементу произвольной версии для мапы:
-```python
-dct.get(version, key)
-```
-
-Добавление элемента в конец в новую версию для массива и списка:
-```python
-arr.add(element)
-lst.add(element)
-```
-
-Добавление элемента в начало в новую версию списка:
-```python
-lst.add_first(element)
-```
-
-Удаление элемента по индексу для массива и списка в новой версии и возвращение элемента:
-```python
-arr.pop(index)
-lst.pop(index)
-```
-
-Удаление элемента по ключу для мапы в новой версии и возвращение элемента:
-```python
-dct.pop(key)
-```
-
-Обновление элемента по индексу в новой версии массива или списка:
-```python
-arr[index] = element
-lst[index] = element
-```
-
-Обновление элемента по ключу в новой версии мапы:
-```python
-dct['key'] = element
-```
-
-Вставка элемента в новую версию по указанному индексу для массива или списка:
-```python
-arr.insert(index, element)
-lst.insert(index, element)
-```
-
-Удаление элемента в новой версии массива или списка по индексу:
-```python
-arr.remove(index)
-lst.remove(index)
-```
-
-Удаление элемента в новой версии мапы по ключу:
-```python
-dct.remove(key)
-```
-
-Получение размера массива или списка для текущей версии:
-```python
-arr.get_size()
-lst.get_size()
-```
-
-Проверка на пустоту для массива или списка:
-```python
-arr.check_is_empty()
-lst.check_is_empty()
-```
-
-Возвращение состояние объекта для указанной версии:
-```python
-arr.get_version(version)
-lst.get_version(version)
-dct.get_version(version)
-```
-
-Обновление текущей версии объекта до указанной:
-```python
-arr.update_version(version)
-lst.update_version(version)
-dct.update_version(version)
-```
-
-Получение элемента текущей версии массива или списка по указанному индексу.
-```python
-arr.__getitem__(index)
-lst.__getitem__(index)
-```
-
-Получение элемента текущей версии мапы по указанному ключу.
-```python
-dct.__getitem__(key)
-```
-
-Обновление или создание элемента по указанному индексу в новой версии для массива или списка.
-```python
-arr.__setitem__(index)
-lst.__setitem__(index)
-```
-
-Обновление или создание элемента по указанному ключу в новой версии для мапы.
-```python
-dct.__setitem__(key)
-```
-
-Очистка объекта, при этом создается новая версия.
-```python
-arr.clear()
-lst.clear()
-dct.clear()
-```
-
----
-## Примеры использования
-
-### Массив (Persistent Array)
-
-**Примеры использования:**
-
-```
- >>> array = PersistentArray(size=5, default_value=0) # Создаем массив из 5 элементов, заполненных нулями
- >>> array[0] # Получаем значение первого элемента
- 0
- >>> array[0] = 10 # Обновляем значение первого элемента в новой версии
- >>> array[0] # Проверяем значение первого элемента в текущей версии
- 10
- >>> array.add(20) # Добавляем новый элемент в конец массива в новую версию
- >>> array[5] # Проверяем значение нового элемента
- 20
- >>> array.pop(1) # Удаляем элемент с индексом 1 в новой версии
- 0
- >>> array.insert(2, 15) # Вставляем значение 15 на индекс 2 в новой версии
- >>> array[2] # Проверяем вставленное значение
- 15
- >>> array.remove(3) # Удаляем элемент с индексом 3
- >>> array.get(1, 0) # Получаем значение элемента с индексом 0 из версии 1
- 0
- >>> array.clear() # Очищаем массив, создавая новую версию
- >>> array.get_size() # Проверяем размер массива в текущей версии
- 0
- >>> array.check_is_empty() # Проверяем, пуст ли массив
- True
- >>> array.get(0, 2) # Пытаемся получить элемент из очищенного массива в версии 0
- Traceback (most recent call last):
- ...
- ValueError: Invalid index
-```
-
-### Список (Persistent Linked List)
-
-**Примеры использования:**
-
-```
- >>> linked_list = PersistentLinkedList([1, 2, 3]) # Создаем список с начальными элементами
- >>> linked_list.add(4) # Добавляем элемент в конец списка
- >>> linked_list[3] # Получаем элемент по индексу в текущей версии
- 4
- >>> linked_list.add_first(0) # Добавляем элемент в начало списка
- >>> linked_list[0] # Получаем элемент в текущей версии по индексу
- 0
- >>> linked_list.insert(2, 1.5) # Вставляем элемент на индекс 2
- >>> linked_list[2] # Проверяем значение вставленного элемента
- 1.5
- >>> linked_list.get(1, 2) # Получаем элемент из версии 1 по индексу 2
- 3
- >>> linked_list.pop(1) # Удаляем элемент по индексу 1 в новой версии
- 2
- >>> linked_list.remove(3) # Удаляем элемент
- >>> linked_list.clear() # Очищаем список, создавая новую версию
- >>> linked_list.get(0, 2) # Пытаемся получить элемент из очищенной версии
- Traceback (most recent call last):
- ...
- IndexError: Index out of range
- >>> linked_list.update_version(2) # Возвращаемся к версии 2
- >>> linked_list[0] # Получаем элемент по индексу в версии 2
- 1
- >>> linked_list.get_size() # Получаем размер списка в текущей версии
- 4
- >>> linked_list.check_is_empty() # Проверяем, пуст ли список в текущей версии
- False
-```
-
-### Aссоциативный массив (Persistent Map)
-
-**Примеры использования:**
-
-```
- >>> map = PersistentMap({'foo': 'bar'}) # Создаем пустой ассоциативный массив
- >>> map['key'] = 'value' # Добавляем элемент
- >>> map['key'] = 'value2' # Изменяем элемент в следующей версии
- >>> map['key'] # Получаем элемент последней версии
- 'value2'
- >>> map.get(1, 'key')
- {'key': 'value'}
- >>> map.remove('key') # Удаляем элемент в новой версии
- >>> map.clear() # Очищаем ассоциативный массив в новой версии
- >>> map.get(0, 'key') # Пытаемся получить элемент отсутствующий в переданной версии
- Traceback (most recent call last):
- ...
- KeyError: Key "key" does not exist
-```
-
----
-## Используемые источники
+Функциональность undo-redo опирается на следующие основные принципы персистентных структур данных:
-1. Milan Straka, "Functional Data Structures and Algorithms", Computer Science Institute of Charles University, Prague 2013
+1. **Ведение истории версий:**
-2. Крис Окасаки, "Чисто функциональные структуры данных", 2018
+ * Изменения в структуре данных записываются как новые версии;
-3. Статья на Geeks for geeks ["Persistent data structures"](https://www.geeksforgeeks.org/persistent-data-structures/)
+ * История версий хранится в виде словаря, где ключами являются номера версий, а значениями — соответствующие состояния.
-4. Несколько статей по персистентным векторам:
+2. **Восстановление состояния:**
- * [Understanding Clojure's Persistent Vectors, pt. 1](https://hypirion.com/musings/understanding-persistent-vector-pt-1)
- * [Understanding Clojure's Persistent Vectors, pt. 2](https://hypirion.com/musings/understanding-persistent-vector-pt-2)
- * [Understanding Clojure's Persistent Vectors, pt. 3](https://hypirion.com/musings/understanding-persistent-vector-pt-3)
+ * Операция undo уменьшает индекс текущей версии, возвращая предыдущее состояние;
-5. Driscoll J. R. et al. Making data structures persistent //Proceedings of the eighteenth annual ACM symposium on Theory of computing. – 1986. – С. 109-121.
+ * Операция redo увеличивает индекс версии, повторяя ранее отменённое состояние.
-6. Серия видео-лекций по персистентным структурам данных:
+3. **Нерушимость изменений:**
- * ["Visualizing Persistent Data Structures" by Dann Toliver](https://www.youtube.com/watch?v=2XH_q494U3U)
- * [Persistent Data Structures, MIT](https://www.youtube.com/watch?v=T0yzrZL1py0)
\ No newline at end of file
+ * Модификации не перезаписывают существующие данные, а создают новую версию с обновлённым состоянием.
\ No newline at end of file
diff --git a/persistent_data_structures/base_persistent.py b/persistent_data_structures/base_persistent.py
index a4b967b..fb8d317 100644
--- a/persistent_data_structures/base_persistent.py
+++ b/persistent_data_structures/base_persistent.py
@@ -15,6 +15,8 @@ def __init__(self, initial_state=None) -> None:
self._history = {0: initial_state}
self._current_state = 0
self._last_state = 0
+ self._container = None
+ self._location = None
def get_version(self, version):
"""Возвращает состояние персистентной структуры данных на указанной версии.
@@ -37,8 +39,33 @@ def update_version(self, version):
raise ValueError(f'Version "{version}" does not exist')
self._current_state = version
+ def undo(self):
+ """Отменяет последнее изменение."""
+ if self._container is not None:
+ raise NotImplementedError(f'Cannot undo inside container "{self._container}"')
+ if self._current_state > 0:
+ self._current_state -= 1
+
+ def redo(self):
+ """Отменяет отмененное изменение."""
+ if self._container is not None:
+ raise NotImplementedError(f'Cannot redo inside container "{self._container}"')
+ if self._current_state < self._last_state:
+ self._current_state += 1
+
def _create_new_state(self) -> None:
"""Создает новую версию."""
+ # Персистентные структуры могут быть вложенными,
+ # поэтому нужно сохранять историю изменений в родительской персистентной структуре
+ if self._container is not None:
+ # В текущей версии родительской структуры подменяем имеющуюся вложенную структуру
+ # на ее копию, таким образом изменения во вложенной структуре не будут отражаться на
+ # прощлых версиях родительской структуры
+ self._container._history[
+ self._container._current_state
+ ][self._location] = deepcopy(self)
self._last_state += 1
self._history[self._last_state] = deepcopy(self._history[self._current_state])
self._current_state = self._last_state
+ if self._container is not None:
+ self._container[self._location] = self
diff --git a/persistent_data_structures/persistent_array.py b/persistent_data_structures/persistent_array.py
index cee913f..d2e7da4 100644
--- a/persistent_data_structures/persistent_array.py
+++ b/persistent_data_structures/persistent_array.py
@@ -54,6 +54,9 @@ def add(self, value: any) -> None:
:param value (int): Значение нового элемента, который добавляется в массив.
"""
self._create_new_state()
+ if isinstance(value, BasePersistent):
+ value._container = self
+ value._location = self.size
self._history[self._last_state] = np.append(self._history[self._last_state], value)
self.size += 1
@@ -70,6 +73,10 @@ def pop(self, index: int) -> any:
self._create_new_state()
self._history[self._last_state] = np.delete(self._history[self._last_state], index)
self.size -= 1
+ # Сдвигаем индексы всех элементов BasePersistent после удаленного элемента
+ for i in range(index, self.size):
+ if isinstance(self._history[self._current_state][i], BasePersistent):
+ self._history[self._current_state][i]._location -= 1
return removed_element
def __setitem__(self, index: int, value: any) -> None:
@@ -83,6 +90,9 @@ def __setitem__(self, index: int, value: any) -> None:
"""
if index < 0 or index >= self.size:
raise ValueError("Invalid index")
+ if isinstance(value, BasePersistent):
+ value._container = self
+ value._location = index
self._create_new_state()
self._history[self._last_state][index] = value
@@ -97,9 +107,16 @@ def insert(self, index: int, value: any) -> None:
"""
if index < 0 or index > self.size:
raise ValueError("Invalid index")
+ if isinstance(value, BasePersistent):
+ value._container = self
+ value._location = index
self._create_new_state()
self._history[self._last_state] = np.insert(self._history[self._last_state], index, value)
self.size += 1
+ # Сдвигаем индексы всех элементов BasePersistent после добавленного элемента
+ for i in range(index+1, self.size):
+ if isinstance(self._history[self._current_state][i], BasePersistent):
+ self._history[self._current_state][i]._location += 1
def remove(self, index: int) -> None:
"""Удаление элемента в новой версии массива по индексу.
diff --git a/persistent_data_structures/persistent_list.py b/persistent_data_structures/persistent_list.py
index 75b4fa0..1943dc7 100644
--- a/persistent_data_structures/persistent_list.py
+++ b/persistent_data_structures/persistent_list.py
@@ -57,6 +57,9 @@ def add(self, data: any) -> None:
"""
self._create_new_state()
head, tail = self._history[self._last_state]
+ if isinstance(data, BasePersistent):
+ data._container = self
+ data._location = self.size
new_node = Node(data)
if tail is None:
head = tail = new_node
@@ -76,6 +79,9 @@ def add_first(self, data: any) -> None:
"""
self._create_new_state()
head, tail = self._history[self._last_state]
+ if isinstance(data, BasePersistent):
+ data._container = self
+ data._location = 0
new_node = Node(data, next_node=head)
if head:
head.prev = new_node
@@ -83,6 +89,13 @@ def add_first(self, data: any) -> None:
if tail is None:
tail = new_node
self.size += 1
+ # сдвигаем индексы персистентных элементов
+ current = head
+ while current.next_node:
+ current = current.next_node
+ if isinstance(current.value, BasePersistent):
+ current.value._location += 1
+
self._history[self._last_state] = (head, tail)
def insert(self, index: int, data: any) -> None:
@@ -94,8 +107,15 @@ def insert(self, index: int, data: any) -> None:
:return: None
:raises IndexError: Если индекс выходит за пределы списка.
"""
+ if index < 0 or index > self.size:
+ raise IndexError("Index out of range")
+
self._create_new_state()
head, tail = self._history[self._last_state]
+ if isinstance(data, BasePersistent):
+ data._container = self
+ data._location = index
+
current = head
count = 0
while current:
@@ -107,11 +127,12 @@ def insert(self, index: int, data: any) -> None:
if current == head:
head = new_node
self.size += 1
- break
+ count += 1
+
+ if isinstance(current.value, BasePersistent) and count > index:
+ current.value._location += 1
count += 1
current = current.next_node
- else:
- raise IndexError("Index out of range")
self._history[self._last_state] = (head, tail)
def pop(self, index: int) -> any:
@@ -122,12 +143,15 @@ def pop(self, index: int) -> any:
:return: Значение удаленного элемента.
:raises IndexError: Если индекс выходит за пределы списка.
"""
- head, tail = self._history[self._current_state]
+ if index < 0 or index >= self.size:
+ raise IndexError("Index out of range")
+ self._create_new_state()
+ head, tail = self._history[self._last_state]
+ popped_value = self.__getitem__(index)
current = head
count = 0
while current:
if count == index:
- value = current.value
if current.prev:
current.prev.next_node = current.next_node
if current.next_node:
@@ -136,40 +160,25 @@ def pop(self, index: int) -> any:
head = current.next_node
if current == tail:
tail = current.prev
- self._create_new_state()
+
self.size -= 1
self._history[self._last_state] = (head, tail)
- return value
+
+ if isinstance(current.value, BasePersistent) and count > index:
+ current.value._location -= 1
count += 1
current = current.next_node
- raise IndexError("Index out of range")
+ return popped_value
- def remove(self, value: any) -> None:
+ def remove(self, index: int) -> None:
"""
- Удаляет элемент из списка в новой версии.
+ Удаляет элемент по индексу из списка в новой версии.
- :param data: Данные элемента для удаления.
+ :param index: Индекс элемента для удаления.
:return: None
- :raises ValueError: Если элемент не найден в списке.
+ :raises IndexError: Если индекс выходит за пределы списка.
"""
- head, tail = self._history[self._current_state]
- current = head
- while current:
- if current.value == value:
- if current.prev:
- current.prev.next_node = current.next_node
- if current.next_node:
- current.next_node.prev = current.prev
- if current == head:
- head = current.next_node
- if current == tail:
- tail = current.prev
- self._create_new_state()
- self.size -= 1
- self._history[self._last_state] = (head, tail)
- return
- current = current.next_node
- raise ValueError(f"Value {value} not found in the list")
+ self.pop(index)
def get(self, version: int = None, index: int = None) -> any:
"""
@@ -234,6 +243,9 @@ def __setitem__(self, index: int, value: any) -> None:
:raises IndexError: Если индекс выходит за пределы списка.
"""
self._create_new_state()
+ if isinstance(value, BasePersistent):
+ value._container = self
+ value._location = index
head, tail = self._history[self._last_state]
current = head
count = 0
diff --git a/persistent_data_structures/persistent_map.py b/persistent_data_structures/persistent_map.py
index 9efb825..7607571 100644
--- a/persistent_data_structures/persistent_map.py
+++ b/persistent_data_structures/persistent_map.py
@@ -20,6 +20,9 @@ def __setitem__(self, key: any, value: any) -> None:
:param value: Значение
"""
self._create_new_state()
+ if isinstance(value, BasePersistent):
+ value._container = self
+ value._location = key
self._history[self._last_state][key] = value
def __getitem__(self, key: any) -> any:
@@ -50,8 +53,11 @@ def pop(self, key: any) -> any:
:param key: Ключ
:return: Удаленный элемент
"""
+ if key not in self._history[self._current_state]:
+ raise KeyError(f'Key "{key}" does not exist')
self._create_new_state()
- return self._history[self._last_state].pop(key)
+ popped_item = self._history[self._last_state].pop(key)
+ return popped_item
def remove(self, key: any) -> None:
"""Удаляет элемент по указанному ключу в новой версии.
@@ -63,4 +69,4 @@ def remove(self, key: any) -> None:
def clear(self) -> None:
"""Очищает ассоциативный массив в новой версии."""
self._create_new_state()
- self._history[self._current_state] = {}
+ self._history[self._last_state] = {}
diff --git a/tests/test_list.py b/tests/test_list.py
index 75f6742..e3a986f 100644
--- a/tests/test_list.py
+++ b/tests/test_list.py
@@ -40,9 +40,9 @@ def test_pop(linked_list):
def test_remove(linked_list):
"""Тест 5. Проверка удаления элемента по значению"""
- linked_list.remove(4)
- with pytest.raises(ValueError):
- linked_list.remove(4)
+ linked_list.remove(1)
+ with pytest.raises(IndexError):
+ linked_list.remove(8)
def test_get(linked_list):