Voici une version légèrement plus complexe d'une classe de liste chaînée, avec une interface similaire aux types de séquence de python (c'est-à-dire prend en charge l'indexation, le découpage, la concaténation avec des séquences arbitraires, etc.). Il devrait avoir O (1) en préfixe, ne copie pas les données sauf si nécessaire et peut être utilisé de manière interchangeable avec les tuples.
Ce ne sera pas aussi efficace en termes d'espace ou de temps que les cellules lisp contre, car les classes python sont évidemment un peu plus lourdes (vous pouvez améliorer légèrement les choses avec " __slots__ = '_head','_tail'
" pour réduire l'utilisation de la mémoire). Il aura cependant les caractéristiques de performances souhaitées en gros O.
Exemple d'utilisation:
>>> l = LinkedList([1,2,3,4])
>>> l
LinkedList([1, 2, 3, 4])
>>> l.head, l.tail
(1, LinkedList([2, 3, 4]))
LinkedList.cons(0, l)
LinkedList([0, 1, 2, 3, 4])
[-1,0] + l
LinkedList([-1, 0, 1, 2, 3, 4])
>>> l[1], l[-1], l[2:]
(2, 4, LinkedList([3, 4]))
>>> assert l[2:] is l.next.next
>>> LinkedList(range(100))[-10::2]
LinkedList([90, 92, 94, 96, 98])
La mise en oeuvre:
import itertools
class LinkedList(object):
"""Immutable linked list class."""
def __new__(cls, l=[]):
if isinstance(l, LinkedList): return l
i = iter(l)
try:
head = i.next()
except StopIteration:
return cls.EmptyList
tail = LinkedList(i)
obj = super(LinkedList, cls).__new__(cls)
obj._head = head
obj._tail = tail
return obj
@classmethod
def cons(cls, head, tail):
ll = cls([head])
if not isinstance(tail, cls):
tail = cls(tail)
ll._tail = tail
return ll
@property
def head(self): return self._head
@property
def tail(self): return self._tail
def __nonzero__(self): return True
def __len__(self):
return sum(1 for _ in self)
def __add__(self, other):
other = LinkedList(other)
if not self: return other
start=l = LinkedList(iter(self))
while l:
if not l._tail:
l._tail = other
break
l = l._tail
return start
def __radd__(self, other):
return LinkedList(other) + self
def __iter__(self):
x=self
while x:
yield x.head
x=x.tail
def __getitem__(self, idx):
"""Get item at specified index"""
if isinstance(idx, slice):
start = idx.start or 0
if (start >= 0) and (idx.stop is None) and (idx.step is None or idx.step == 1):
no_copy_needed=True
else:
length = len(self)
start, stop, step = idx.indices(length)
no_copy_needed = (stop == length) and (step == 1)
if no_copy_needed:
l = self
for i in range(start):
if not l: break
l=l.tail
return l
else:
if step < 1:
return LinkedList(list(self)[start:stop:step])
else:
return LinkedList(itertools.islice(iter(self), start, stop, step))
else:
if idx < 0: idx = len(self)+idx
if not self: raise IndexError("list index out of range")
if idx == 0: return self.head
return self.tail[idx-1]
def __mul__(self, n):
if n <= 0: return Nil
l=self
for i in range(n-1): l += self
return l
def __rmul__(self, n): return self * n
def __hash__(self): return hash(tuple(self))
def __eq__(self, other): return self._cmp(other) == 0
def __ne__(self, other): return not self == other
def __lt__(self, other): return self._cmp(other) < 0
def __gt__(self, other): return self._cmp(other) > 0
def __le__(self, other): return self._cmp(other) <= 0
def __ge__(self, other): return self._cmp(other) >= 0
def _cmp(self, other):
"""Acts as cmp(): -1 for self<other, 0 for equal, 1 for greater"""
if not isinstance(other, LinkedList):
return cmp(LinkedList,type(other))
A, B = iter(self), iter(other)
for a,b in itertools.izip(A,B):
if a<b: return -1
elif a > b: return 1
try:
A.next()
return 1
except StopIteration: pass
try:
B.next()
return -1
except StopIteration: pass
return 0
def __repr__(self):
return "LinkedList([%s])" % ', '.join(map(repr,self))
class EmptyList(LinkedList):
"""A singleton representing an empty list."""
def __new__(cls):
return object.__new__(cls)
def __iter__(self): return iter([])
def __nonzero__(self): return False
@property
def head(self): raise IndexError("End of list")
@property
def tail(self): raise IndexError("End of list")
LinkedList.EmptyList = EmptyList()
del EmptyList