2012-06-29 13 views
6

Jak mogę utworzyć odpowiednik Pythona dla pdtolist z Pop-11?Dynamiczna lista, która automatycznie rozwija się

Załóżmy, że mam generator o nazwie g, który zwraca (powiedzmy) liczby całkowite po jednym na raz. Chciałbym utworzyć listę a, która rośnie automatycznie, gdy pytam o wartości poza bieżącym końcem listy. Na przykład:

print a # => [ 0, 1, 2, g] 
print a[0] # => 0 
print a[1] # => 1 
print a[2] # => 2 
# (obvious enough up to here) 

print a[6] # => 6 
print a # => [ 0, 1, 2, 3, 4, 5, 6, g] 
# list has automatically expanded 

a = a[4:] # discard some previous values 
print a # => [ 4, 5, 6, g] 
print a[0] # => 4 

Terminologia - przewidywanie prawdopodobnego nieporozumień: lista jest „dynamiczna tablica”, ale to nie to, co mam na myśli; Chciałbym "listy dynamicznej" w bardziej abstrakcyjnym znaczeniu.

Aby lepiej wytłumaczyć motywację, załóżmy, że masz 999999999 przedmiotów do przetworzenia. Próba dopasowania wszystkich do pamięci (na normalnej liście) naraz byłaby wyzwaniem. Generator rozwiązuje tę część problemu, przedstawiając je pojedynczo; każdy tworzony na żądanie lub czytany indywidualnie z dysku. Ale przypuśćmy, że podczas przetwarzania chcesz odwoływać się do ostatnich wartości, a nie tylko do bieżących? Możesz zapamiętać ostatnie (powiedzmy) dziesięć wartości na osobnej liście. Ale lista dynamiczna jest lepsza, ponieważ zapamiętuje je automatycznie.

+0

Zastępuje metodę '__getitem__' listy w celu przechwycenia" IndexError ". –

+0

Masz więc listę 'L' i wykonaj' L [999999999] '- lista powinna stać się tą długością? –

+0

Tak, w zasadzie. Zapomnij programistę! –

Odpowiedz

2

Wielkie dzięki dla wszystkich, którzy przyczynili się do powstania pomysłów! Oto, co zebrałem ze wszystkich odpowiedzi. Zachowuje to większość funkcjonalności z normalnej klasy list, dodając dodatkowe zachowania tam, gdzie jest to konieczne, aby spełnić dodatkowe wymagania.

class DynamicList(list): 
    def __init__(self, gen): 
     self.gen = gen 

    def __getitem__(self, index): 
     while index >= len(self): 
      self.append(next(self.gen)) 
     return super(DynamicList, self).__getitem__(index) 

    def __getslice__(self, start, stop): 
     # treat request for "last" item as "most recently fetched" 
     if stop == 2147483647: stop = len(self) 
     while stop > len(self): 
      self.append(next(self.gen)) 
     return super(DynamicList, self).__getslice__(start, stop) 

    def __iter__(self): 
     return self 

    def next(self): 
     n = next(self.gen) 
     self.append(n) 
     return n 

a = DynamicList(iter(xrange(10))) 

Wcześniej generowane wartości można uzyskać pojedynczo jako elementy lub wycinki. Zarejestrowana historia rozwija się w razie potrzeby, jeśli żądane elementy znajdują się poza bieżącym końcem listy. Dostęp do całej zapisanej historii można uzyskać za jednym razem, używając print a lub przypisanej do zwykłej listy przy użyciu b = a[:]. Fragment zarejestrowanej historii można usunąć przy użyciu del a[0:4]. Możesz przeglądać całą listę przy użyciu for, usuwając ją w dowolnym momencie lub w dowolnym momencie. Jeśli dojdziesz do końca generowanych wartości, podniesiona zostanie StopIteration.

Trochę niezręczności. Przydziały takie jak a = a[0:4] pomyślnie obcinają historię, ale wynikowa lista nie jest już automatycznie rozwijana. Zamiast tego użyj del a[0:4], aby zachować właściwości automatycznego wzrostu. Ponadto, nie jestem całkowicie zadowolony z konieczności rozpoznania wartości magicznej, 2147483647, reprezentującej najnowszy przedmiot.

2

To może Ci zacząć:

class DynamicList(list): 
    def __init__(self, gen): 
     self._gen = gen 

    def __getitem__(self, index): 
     while index >= len(self): 
      self.append(next(self._gen)) 
     return super(DynamicList, self).__getitem__(index) 

Musisz dodać trochę specjalnego traktowania na plasterki (obecnie, po prostu wrócić do normalnej listy, więc można stracić dynamiczne zachowanie). Ponadto, jeśli chcesz, aby sam generator był elementem listy, będzie to trochę skomplikowane.

+0

Nie powinna to być podklasa listy; '__init__' jest niekompatybilny i nie powinien obsługiwać' __len__' lub '__setitem__' ... – agf

+0

@agf: Problem' __init__' jest prawidłowym punktem, ale dlaczego nie powinien obsługiwać '__len__' lub' __setitem__'? – voithos

+0

Jego długość jest nieznana, ponieważ długość generatora jest nieznana i powinna odzwierciedlać tylko elementy z generatora, więc nie powinno być możliwe przypisanie do niego. – agf

1

Właśnie odpowiedziałem na podobne pytanie i zdecydowałem się zaktualizować moją odpowiedź dla ciebie to pokazuje?

class dynamic_list(list): 
    def __init__(self,num_gen): 
     self._num_gen = num_gen 
    def __getitem__(self,index): 
     if isinstance(index, int): 
      self.expandfor(index) 
      return super(dynamic_list,self).__getitem__(index) 

     elif isinstance(index, slice): 
      if index.stop<index.start: 
       return super(dynamic_list,self).__getitem__(index) 
      else: 
       self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start) 
      return super(dynamic_list,self).__getitem__(index) 

    def __setitem__(self,index,value): 
     if isinstance(index, int): 
      self.expandfor(index) 
      return super(dynamic_list,self).__setitem__(index,value) 

     elif isinstance(index, slice): 
      if index.stop<index.start: 
       return super(dynamic_list,self).__setitem__(index,value) 
      else: 
       self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start) 
      return super(dynamic_list,self).__setitem__(index,value) 

    def expandfor(self,index): 
      rng = [] 
      if abs(index)>len(self)-1: 
       if index<0: 
        rng = xrange(abs(index)-len(self)) 
       else: 
        rng = xrange(abs(index)-len(self)+1) 
      for i in rng: 
       self.append(self._num_gen.next()) 
Powiązane problemy