Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
370 changes: 16 additions & 354 deletions README.md

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions persistent_data_structures/base_persistent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""Возвращает состояние персистентной структуры данных на указанной версии.
Expand All @@ -27,6 +29,22 @@ def get_version(self, version):
raise ValueError(f'Version "{version}" does not exist')
return self._history[version]

def getcopy(self, version: int, key: any) -> any:
"""Возвращает копию элемента с указанной версией и ключом/индексом.

:param version: Номер версии
:param key: Ключ/индекс
:return: Копия значения сответствующее указанному ключу или None,
если ключ/индекс не существует.
:raises ValueError: Если версия не существует
:raises KeyError: Если ключ/индекс не существует
"""
value = deepcopy(self.get(version, key))
if isinstance(value, BasePersistent):
value._container = None
value._location = None
return value

def update_version(self, version):
"""Обновляет текущую версию персистентной структуры данных до указанной.

Expand All @@ -37,8 +55,38 @@ 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:
raise ValueError("No actions to undo")
if self._current_state > 0:
self._current_state -= 1

def redo(self):
"""Повторяет последнее отмененное изменение.

:raises ValueError: Если нет операций."""
if self._container is not None:
raise NotImplementedError(f'Cannot redo inside container "{self._container}"')
if self._current_state >= self._last_state:
raise ValueError("No operations to redo")
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
19 changes: 18 additions & 1 deletion persistent_data_structures/persistent_array.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np

from persistent_data_structures.base_persistent import BasePersistent
from base_persistent import BasePersistent


class PersistentArray(BasePersistent):
Expand Down Expand Up @@ -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

Expand All @@ -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:
Expand All @@ -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

Expand All @@ -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:
"""Удаление элемента в новой версии массива по индексу.
Expand Down
143 changes: 112 additions & 31 deletions persistent_data_structures/persistent_list.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from persistent_data_structures.base_persistent import BasePersistent
from copy import deepcopy

from base_persistent import BasePersistent


class Node:
Expand Down Expand Up @@ -57,6 +59,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
Expand All @@ -76,13 +81,23 @@ 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
head = new_node
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:
Expand All @@ -94,8 +109,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:
Expand All @@ -107,11 +129,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:
Expand All @@ -122,12 +145,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:
Expand All @@ -136,40 +162,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:
"""
Expand Down Expand Up @@ -234,6 +245,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
Expand All @@ -255,6 +269,20 @@ def get_size(self) -> int:
"""
return self.size

def calc_size(self) -> int:
"""
Расчет текущего размера списка.

:return: Количество элементов в текущей версии списка.
"""
size = 0
head, tail = self._history[self._current_state]
current = head
while current:
size += 1
current = current.next_node
return size

def check_is_empty(self) -> bool:
"""
Проверяет, пуст ли список.
Expand All @@ -263,3 +291,56 @@ def check_is_empty(self) -> bool:
"""
head, tail = self._history[self._current_state]
return head is None

def update_version(self, version) -> None:
"""Обновляет текущую версию персистентной структуры данных до указанной.

:param version: Номер версии.
:raises ValueError: Если указанная версия не существует.
"""
if version < 0 or version >= len(self._history):
raise ValueError(f'Version "{version}" does not exist')
self._current_state = version
self.size = self.calc_size()

def undo(self) -> None:
"""Отменяет последнее изменение."""
if self._container is not None:
raise NotImplementedError(f'Cannot undo inside container "{self._container}"')
if self._current_state == 0:
raise ValueError("No actions to undo")
if self._current_state > 0:
self._current_state -= 1
self.size = self.calc_size()

def redo(self) -> None:
"""Повторяет последнее отмененное изменение.

:raises ValueError: Если нет операций."""
if self._container is not None:
raise NotImplementedError(f'Cannot redo inside container "{self._container}"')
if self._current_state >= self._last_state:
raise ValueError("No operations to redo")
self._current_state += 1
self.size = self.calc_size()

def _create_new_state(self) -> None:
"""Создает новую версию."""
# Персистентные структуры могут быть вложенными,
# поэтому нужно сохранять историю изменений в родительской персистентной структуре
if self._container is not None:
# В текущей версии родительской структуры подменяем имеющуюся вложенную структуру
# на ее копию, таким образом изменения во вложенной структуре не будут отражаться на
# прощлых версиях родительской структуры
head, tail = self._container._history[self._container._current_state]
current = head
while current:
if current.value is self:
current.value = deepcopy(self)
break
current = current.next_node
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
Loading
Loading