2013-06-16 32 views
7

Python ma elegancki sposób automatycznego generowania zmiennej licznika w pętlach for: funkcja enumerate. To oszczędza potrzebę inicjowania i zwiększania zmiennej licznika. Zmienne licznika są również brzydkie, ponieważ często są bezużyteczne po zakończeniu pętli, ale ich zasięg nie jest zasięgiem pętli, więc zajmują przestrzeń nazw bez potrzeby (chociaż nie jestem pewien, czy to rozwiązuje w rzeczywistości enumerate).Wyliczanie pythoniczne pętli while

Moje pytanie brzmi, czy istnieje podobne rozwiązanie pytonowe dla pętli while. enumerate nie będzie działać dla pętli while od enumerate zwraca iterator. Idealnie, rozwiązanie powinno być "pytonowe" i nie wymagać definicji funkcji.

Na przykład:

x=0 
c=0 
while x<10: 
    x=int(raw_input()) 
    print x,c 
    c+=1 

W tym przypadku chcielibyśmy, aby uniknąć inicjalizacji i inkrementacji c.

Wyjaśnienie:

Można to zrobić z niekończącej się pętli for z zakończeniem ręcznym jak niektórzy sugerowali, ale szukam rozwiązania, które sprawia, że ​​kod wyraźniejsze, i nie sądzę, że rozwiązanie sprawia, że ​​kod jest wyraźniejszy w tym przypadku.

+5

Czy możesz podać przykład? Dlaczego musisz używać pętli while? – BrenBarn

+4

Szukasz czegoś takiego jak [itertools.count?] (Http://docs.python.org/2.7/library/itertools.html#itertools.count) –

+0

Czy masz na myśli, że podczas (i <100) {i ++} rodzaju pętli? – Darek

Odpowiedz

6

Improvement (w czytelności, powiedziałbym) do Ignacio za odpowiedź:

x = 0 
for c in itertools.takewhile(lambda c: x < 10, itertools.count()): 
    x = int(raw_input()) 
    print x, c 

Zalety:

  • Tylko warunek pętli while znajduje się w nagłówku pętli, a nie efekt uboczny raw_input.
  • Stan pętli może zależeć od dowolnego warunku, jaki może mieć normalna pętla while. Nie jest konieczne "importowanie" zmiennych, do których odwołuje się takt, ponieważ są one już widoczne w zakresie lambda. Dodatkowo może zależeć od liczby, jeśli chcesz, ale nie w tym przypadku.
  • Uproszczony: wyliczenie już nie występuje.
+0

Ładne rozwiązanie. Myślę, że czytelność jest całkiem dobra. – Bitwise

1

Nie sądzę, że można robić to, co chcesz, dokładnie tak, jak chcesz. Jeśli dobrze rozumiem, potrzebujesz pętli while, która za każdym razem zwiększa licznik, bez wystawiania widocznego licznika poza zasięg pętli. Myślę, że sposobem na zrobienie tego byłoby przepisanie pętli while jako nieterminowej pętli for i ręczne sprawdzenie warunku końcowego. Przykładowy kod:

import itertools 

x = 0 
for c in itertools.count(): 
    x = int(raw_input()) 
    print x, c 
    if x >= 10: 
     break 

Problem polega na tym, że zasadniczo wykonujesz iterację z licznikiem. Jeśli nie chcesz wystawiać tego licznika, musi on pochodzić z konstrukcji pętli. Bez zdefiniowania nowej funkcji utkniesz w standardowej pętli i wyraźnym sprawdzeniu.

Z drugiej strony prawdopodobnie można również zdefiniować do tego generator. Nadal będziesz iterować, ale możesz przynajmniej zawrzeć sprawdzanie w konstrukcji pętli.

+0

Dzięki. Generalnie staram się unikać tego rodzaju pętli 'for' i myślę, że nie jest to bardzo pyton. Prawdopodobnie byłoby bardziej przejrzyste po prostu zachować zmienną 'while' i licznik. Zobaczmy, czy ktoś wymyśli inne rozwiązanie. – Bitwise

6

Znów z itertools ...

import itertools 

for c, x in enumerate(
    itertools.takewhile(lambda v: v < 10, 
     (int(raw_input()) for z in itertools.count()) 
    ) 
): 
    print c, x 
+0

Interesujące, ale nie powiedziałbym, że to czyni kod jaśniejszym! :-) – BrenBarn

+1

@BrenBarn: To ... może znieść * bit * refaktoryzacji ... –

+0

W rzeczywistości nie jest całkiem funkcjonalnie taki sam. Oryginalny kod wyświetli 10 lub większą wartość, ale ta nie będzie. – morningstar

2

Jeśli chcesz zerowy inicjalizacji przed pętli while, można użyć Singleton z licznikiem:

class Singleton(object): 
    _instance = None 
    def __new__(cls, *args, **kwargs): 
     if not cls._instance: 
      cls._instance = super(Singleton, cls).__new__(
           cls, *args, **kwargs) 
      cls.count=0 
     else: 
      cls.count+=1        
     return cls._instance 

to nie będzie tylko jedno wystąpienie Singleton i każdy dodatkowy przykład tylko dodaje jeden:

>>> Singleton().count # initial instance 
0 
>>> Singleton().count 
1 
>>> Singleton().count 
2 
>>> Singleton().count 
3 

Następnie pętla natomiast staje:

while Singleton(): 
    x=int(raw_input('x: ')) 
    if x>10: break 

print 'While loop executed',Singleton().count,'times' 

Wprowadzanie 1,2,3,11 drukuje:

x: 1 
x: 2 
x: 3 
x: 11 
While loop executed 4 times 

Jeśli nie przeszkadza pojedynczy inicjalizacji wiersz przed pętli while, można po prostu podklasy się interator:

import collections 
class WhileEnum(collections.Iterator): 
    def __init__(self,stop=None): 
     self.stop=stop 
     self.count=0 

    def next(self): # '__next__' on Py 3, 'next' on Py 2 
     if self.stop is not None: 
      self.remaining=self.stop-self.count 
      if self.count>=self.stop: return False 
     self.count+=1  
     return True 

    def __call__(self): 
     return self.next() 

wówczas pętli while staje się:

enu=WhileEnum() 
while enu(): 
    i=int(raw_input('x: ')) 
    if i>10: break 

print enu.count 

Myślę, że drugie jest o wiele lepszym podejściem. Można mieć wiele rachmistrzów można także ustawić limit na ile pętle, aby przejść:

limited_enum=WhileEnum(5) 
+0

Interesujące ujęcie, dzięki. – Bitwise

Powiązane problemy