Структура данных для массивов, которые разделяют некоторые элементы - Python - PullRequest
0 голосов
/ 24 августа 2018

У меня есть коллекция массивов, которые "перекрываются" в определенных элементах.Вот иллюстрация примера, включающего 3 массива символов:

  array0↓
       'A'      ↓array2
array1→'B' 'D' 'E'
       'C'     'F'

Важно то, что изменения в массивах должны учитывать эту структуру.Так, например, если я изменю «B» в массиве 0 на «X», «B» в массиве 1 также должен измениться на «X».

Мой вопрос заключается в том, что является хорошим и эффективным способом реализацииэто в Python?

До сих пор я думал о двух вещах:

Во-первых, я могу создать класс на заказ, экземпляры которого содержат совершенно отдельный список вместе с информацией о любомперекрывает его и соответствующим образом реализует методы обновления, так что любое изменение в списке всегда дублируется для других списков в перекрытиях.Это кажется немного перегруженным, и включает в себя дублирование данных.

Два, я мог бы сделать это с помощью одноэлементных списков, таких как:

data = [['A'], ['B'], ['C'], ['D'], ['E'], ['F']]
array0 = [data[0], data[1], data[2]]
array1 = [data[1], data[3], data[4]]
array2 = [data[4], data[5]]

for array in array0, array1, array2:
     print(array)

>>> [['A'], ['B'], ['C']]
>>> [['B'], ['D'], ['E']]
>>> [['E'], ['F']]

array0[1][0] = 'X'

for array in array0, array1, array2:
     print(array)

>>> [['A'], ['X'], ['C']]
>>> [['X'], ['D'], ['E']]
>>> [['E'], ['F']]

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

Ответы [ 5 ]

0 голосов
/ 24 августа 2018

Мое предложение - это вариант, предложенный @a_guest.У вас может быть класс-оболочка, который помечает элементы как общие и структура данных для обработки таких элементов:

class SharedElement:
    def __init__(self, val):
        self.val = val

    def update(self, val):
        self.val = val

    def __repr__(self):
        return "SharedElement({0})".format(self.val)

    def __str__(self):
        return str(self.val)


class SharedList:
    def __init__(self, lst):
        self._lst = lst

    def __getitem__(self, item):
        if isinstance(self._lst[item], SharedElement):
            return self._lst[item].val
        return self._lst[item]

    def __setitem__(self, key, value):
        if isinstance(self._lst[key], SharedElement):
            self._lst[key].update(value)


B = SharedElement('B')
E = SharedElement('E')

a = SharedList(['A', B, 'C'])
b = SharedList([B, 'D', E])
c = SharedList([E, 'F'])

b[0] = 'X'

print([val for val in a])
print([val for val in b])
print([val for val in c])

Вывод

['A', 'X', 'C']
['X', 'D', 'E']
['E', 'F']
0 голосов
/ 24 августа 2018

Вы можете создать подкласс list и использовать выделенный класс-оболочку для прокси общего содержимого. Это не требует дублирования данных, так как он хранит только прокси для общих данных, которые отправляются на исходные данные. Это немного похоже на ваш подход с вложенными списками, но поддерживает нормальный интерфейс списка. Вот пример реализации:

class Intersection:
    def __init__(self, other, index):
        self.other = other
        self.index = index

    def __repr__(self):
        return repr(self.other[self.index])

    @property
    def value(self):
        return self.other[self.index]

    @value.setter
    def value(self, v):
        self.other[self.index] = v


class List(list):
    def __getitem__(self, index):
        item = super().__getitem__(index)
        return item.value if isinstance(item, Intersection) else item

    def __setitem__(self, index, value):
        item = super().__getitem__(index)
        if isinstance(item, Intersection):
            item.value = value
        else:
            super().__setitem__(index, value)

    def share(self, index):
        return Intersection(self, index)

Теперь вы можете обмениваться данными между списками по мере необходимости:

a = List(['A', 'B', 'C'])
b = List([a.share(1), 'D', 'E'])
c = List([b.share(2), 'F'])

a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)

Что дает в качестве вывода:

['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
0 голосов
/ 24 августа 2018

Как вы указали в своем вопросе, соответствующая информация такова:

array0ptr = [0, 1, 2]
array1ptr = [1, 3, 4]
array2ptr = [4, 5]

(я добавляю суффикс ptr, потому что практически эти элементы являются указателями).Здесь элемент списка - это указатель на объекты, которые должны храниться в отдельном списке.

ol = ['A', 'B', 'C', 'D', 'E']

Реальные массивы могут быть получены во время выполнения такими функциями-членами, как

array0 = []
for i in range(len(array0ptr)):
    array0.append(ol[array0ptr[i]])

ТеперьВаша точка зрения: предположим, что список объектов становится

ol = ['A', 'B', 'intruder', 'C', 'D', 'E']

Как мне автоматически отслеживать это в моих массивах ??Эти массивы должны выглядеть так:

array0ptr = [0, 1, 3]
array1ptr = [1, 4, 5]
array2ptr = [5, 6]

Я думаю, что самый простой ответ: оставьте список фиксированным !, и не допускайте вставки или изменения порядка элементов.Просто создайте другой хэш с позицией объекта.В приведенном выше случае у вас будет

sl = ['A', 'B', 'C', 'D', 'E', 'intruder']
slorder = [0, 1, 3, 4, 5, 2]

, тогда можно будет писать функции-члены, которые выводят обновленный список объектов, массив не изменится.Что может быть сложно, если вы хотите удалить объекты, но это сложно в любом случае, я боюсь.

0 голосов
/ 24 августа 2018

Вы можете использовать выделенный класс, который соответствующим образом обновляет другие пересекающиеся экземпляры, как вы указали в своей первой идее.Я бы не стал считать дублирование данных проблемой, так как для изменяемых данных вы в любом случае сохраняете ссылки, и в случае, если вы используете большие неизменяемые данные, вы можете использовать выделенный класс-оболочку (например, в Python 3.7 появился декоратор @dataclass).

Вот пример реализации:

from collections import defaultdict

class List(list):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._intersections = defaultdict(list)

    def __setitem__(self, index, value):
        super().__setitem__(index, value)
        for i, other in self._intersections[index]:
            other[i] = value

    def intersect(self, other, at):
        self._intersections[at[0]].append((at[1], other))

При этом вы можете пересекать списки, как в вашем примере:

a = List(['A', 'B', 'C'])
b = List(['B', 'D', 'E'])
c = List(['E', 'F'])

a.intersect(b, (1, 0))
b.intersect(c, (2, 0))

a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)

, что дает в качестве вывода:

['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
0 голосов
/ 24 августа 2018

Вы можете создать класс-оболочку, который может обрабатывать обновление всех элементов с одинаковым значением:

arr = [[['A'], ['B'], ['C']], [['B'], ['D'], ['E']], [['E'], ['F']]]
class WrapArray:
  def __init__(self, _data):
    self.d = _data
  def __getitem__(self, _x):
    self.x = _x
    class _wrapper:
      def __init__(self, _inst):
         self.ref = _inst
      def __setitem__(self, _y, _val):
         _place = self.ref.d[self.ref.x][_y][0]
         self.ref.d[self.ref.x][_y][0] = _val
         for i in range(len(self.ref.d)):
           for b in range(len(self.ref.d[i])):
             if self.ref.d[i][b][0] == _place:
               self.ref.d[i][b] = [_val]
    return _wrapper(self)
  def __repr__(self):
    return str(self.d)

array = WrapArray(arr)
array[1][0] = 'X'

Выход:

[[['A'], ['X'], ['C']], [['X'], ['D'], ['E']], [['E'], ['F']]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...