2010-04-29 14 views
18

(nawet tytuł ten będzie powodować płomienie, zdaję sobie sprawę)Symulacja C-styl dla pętli w python

Python dokonał świadomego wyboru projektu do skorzystania for pętla wyraźne iterables, z korzyścią znacznie uproszczony kod w większości przypadków.

Jednak czasami jest to dość uciążliwe skonstruować iterable jeśli przypadek testowy i funkcja aktualizacji są skomplikowane i tak znalazłem się na piśmie następujące pętli while:

val = START_VAL 
while <awkward/complicated test case>: 
    # do stuff 
    ... 
    val = <awkward/complicated update> 

Problem polega na tym, że aktualizacja jest w dolnej części bloku while, co oznacza, że ​​jeśli chcę mieć continue osadzone gdzieś w nim muszę:

  • stosowania duplikat kod skomplikowanej/awkard zaktualizować, a

  • ryzykujemy zapominając go i posiadające mój kod nieskończoną pętlę

mogłem przejść trasę ręcznie toczenia skomplikowanej iterator:

def complicated_iterator(val): 
    while <awkward/complicated test case>: 
     yeild val 
     val = <awkward/complicated update> 

for val in complicated_iterator(start_val): 
    if <random check>: 
     continue # no issues here 
    # do stuff 

To wydaje mi się ceniła zbyt gadatliwy i skomplikowany. Czy użytkownicy przepełnienia stosu mają prostszą propozycję?

odpowiedzi na komentarze:

@Glenn Maynard: Tak, ja oddalił odpowiedź. Złe jest pisanie pięciu wierszy, jeśli istnieje sposób na zrobienie tego w jednym ... szczególnie w przypadku, który pojawia się cały czas (pętla jest wspólną cechą programów Turinga-Kompletnych).

Dla osób szukających konkretnego przykładu: załóżmy, że pracuję z niestandardową biblioteką dat. Moje pytanie będzie wówczas, jak można wyrazić to w Pythonie:

for (date = start; date < end; date = calendar.next_quarter_end(date)): 
    if another_calendar.is_holiday(date): 
     continue 
    # ... do stuff... 
+0

sklepu starą val i natychmiast zaktualizować val w następnym wierszu? i używacie oldwalstwa wszędzie, gdzie używaliście val? –

+5

Prawdziwy przykład kodu byłby miły. Mam _never_ musiałem napisać kod, który jest niezręcznie wyglądający w Pythonie ... –

+0

Co jest nie tak z pierwszym? Potrzebujesz wszystkich tych elementów. Czy to tylko, że C pozwala na wszystko w jednym wierszu, a python nie? – phkahler

Odpowiedz

25

To jest najlepsze, co mogę wymyślić:

def cfor(first,test,update): 
    while test(first): 
     yield first 
     first = update(first) 

def example(blah): 
    print "do some stuff" 
    for i in cfor(0,lambda i:i<blah,lambda i:i+1): 
     print i 
    print "done" 

życzę pyton miał składnię wyrażeń closured.

Edytuj: Należy również pamiętać, że wystarczy zdefiniować cfor once (w przeciwieństwie do funkcji complicated_iterator).

+2

Nie mogę zrozumieć, dlaczego Guido jest na "lambda", to bardzo przydatne! –

+0

Jest to bardzo przydatne! – YGA

+0

Dla celów archiwalnych, po prostu dodałem cfor (to samo imię parzyste) do naszego drzewa centralnego i już używałem go w dwóch osobnych skryptach. Dzięki! – YGA

3

Można użyć try/finally klauzulę, aby wykonać aktualizację:

val = START_VAL 

while <awkward/complicated test case>: 
    try: 
     # do stuff 
     continue 

    finally: 
     val = <awkward/complicated update> 

Zastrzeżenie: to również wykonać instrukcję aktualizacji jeśli zrobisz break.

4

Jestem trochę zdezorientowany: masz skomplikowaną ekspresję i skomplikowaną kolejną ekspresję, ale ładnie pasują do pętli C? To nie ma dla mnie sensu.

Polecam niestandardowe podejście iteracyjne. Prawdopodobnie znajdziesz inne zastosowania iteratora, a enkapsulacja iteracji jest i tak dobrą praktyką.

AKTUALIZACJA: Korzystając z przykładu, zdecydowanie zrobiłbym niestandardowy iterator. Wydaje się zupełnie naturalne mi się, że kalendarz będzie w stanie wygenerować serię terminach kwartalnych:

class Calendar: 
    # ... 

    def quarters(self, start, end): 
     """Generate the quarter-start dates between `start` and `end`.""" 
     date = start 
     while date < end: 
      yield date 
      date = self.next_quarter_end(date) 


for date in calendar.quarters(start, end): 
    if another_calendar.is_holiday(date): 
     continue 
    # ... do stuff... 

Wydaje się wspaniałą abstrakcji dla klasy kalendarzowym świadczenia, i założę go używać więcej niż pewnego razu.

+0

Cześć Ned, Dodałem komentarz zawieszający główne pytanie z przykładem "niezręcznego" następnego wyrażenia, które jest wciąż względnie zwarte. – YGA

+0

@YGA: Zaktualizowałem moją odpowiedź. –

+0

Problem polega na tym, że istnieją różne możliwe sekwencje dat, a zresztą niekoniecznie jest jasne, że kontrolujesz klasę kalendarza, więc musisz napisać opakowanie ... – YGA

4

Co o:

date = start 

while date < end: 

    if not another_calendar.is_holiday(date): 
     # ... do stuff... 

    date = calendar.next_quarter_end(date) 

Ale jeśli używasz że szczególną konstrukcją często, jesteś lepiej definiowania generator raz i ponownego wykorzystania go jako zrobiłeś w swoim pytaniu.

(Faktem jest, że ponieważ są to różne języki, nie ma możliwości, aby każdy konstrukt na mapie C zawierał bardziej zwartą konstrukcję w języku Python, podobnie jak twierdzenie, że algorytm kompresji działa równie dobrze na wszystkich losowych wejścia)

+0

C's for loop jest, dla większości celów i celów, po prostu cukier syntaktyczny dla pętli while (włączając pętlę C while's). Przykładowy kod podany w dolnej części pytania OP jest równoważny pokazanej tu pętli while, która dla mnie jest w przybliżeniu tak lapidarna i łatwa do odczytania, jak pętla C dla pętli. –

+0

W większości się zgadzam, chociaż gdzie dialekty C dopuszczają 'for (int idx = 0, etc), to ma to tę zaletę, że ogranicza zasięg licznika tylko do pętli.Nie ma to znaczenia w Pythonie, gdzie 'for' nie tworzy nowego zakresu. (Zwróć także uwagę, że moja uwaga dotycząca zwartości nie jest osądem wartości, tj. Nie mówię, że "kompaktowy == lepiej", nawet jeśli wydaje się, że jest to kryterium OP.) – detly

+0

Robię to w niektórych przypadkach, ale jest rozsądnie często mam wszystkie rodzaje "przypadków testowych" w ciele pętli while. W pewnym sensie uważam, że właśnie dlatego wynaleziono konstrukcję "kontynuuj"; ponieważ osadzenie całej tej logiki w, jeśli instrukcje na górze pętli jest bardzo niezręczne ... – YGA

2

ja często

while True: 
    val = <awkward/complicated update> 
    if not val: 
    break 

    etc. 
0

heh.

def forre(a,i,c,top,increment,run): 
    increment = increment.replace("++","+=1").replace("--","-=1").replace("; ","") 
    while i != top: 
     try: exec(run) 
     except: print "error: "; print run 
     try: exec(increment) 
     except: print "error: "; print increment 

forre("int i=",0,"; i<",6,"; i++", 
    "print i" 
    )