2013-04-30 10 views
5

Potrzebuję zbudować generator i szukałem sposobu na skrócenie tego dla pętli w jedną linię. Próbowałem wyliczyć, ale to nie zadziałało.Czy istnieje sposób na skrócenie tego wyrażenia generatora Pythona?

counter=0 
for element in string: 
    if function(element): 
     counter+=1 
     yield counter 
    else: 
     yield counter 
+1

Brakuje "licznika = 0", czy jest to zamknięcie wokół 'counter' znajduje się w zewnętrznym kodzie? – abarnert

+0

Jaką wersję Python? – Blender

+0

Nie, początkowo to wykluczyłem, ale właśnie go zawarłem. – garlfd

Odpowiedz

6
counter=0 
for element in string: 
    counter+=bool(function(element)) 
    yield counter 

(Tak, dodając do wartości logiczne wskazówki działa dokładnie tak, jak gdyby był 1True i False był 0).

bool() połączenie jest konieczne tylko w przypadku function() może mieć wartości zwracanych inne niż True, False, 1 i 0.

4

Jeśli używasz Python 3, można zrobić:

from itertools import accumulate 

yield from accumulate(1 if function(x) else 0 for x in string) 

Chociaż użyję Simeon Visser's answer. Chociaż ten może być krótki, nie jest od razu jasne, co robi kod.

+0

@jamylak: Na końcu nie pozostawiasz zmiennej 'counter'. – Blender

+0

To nie zadziała, chyba że 'funkcja' jawnie zwróci 0 i 1. Jeśli zwróci, powiedz 'Brak' lub ciąg, otrzymasz' TypeError's po wszystkim. Nawet jeśli zwraca 'True' i' False', pierwszą wartością będzie 'False' zamiast' 0'. – abarnert

+0

Nie jestem pewien, co "zakładam' kumulować' zwraca boolean "oznacza. Zwraca iterator. I jest iteratorem nad jakimkolwiek dodatkowym typem, który mu dajesz (lub raczej nad wynikiem dodania dowolnego typu, który ci dajesz - nie całkiem to samo, ponieważ, np. 'False + True == 1' lub, w starszym Python, '(1 << 31) + (1 << 31) = (1L << 32)'). – abarnert

3

Można skrócić go:

counter=0 
for element in string: 
    if function(element): 
     counter+=1 
    yield counter 
+0

Potrzebuję zachować liczenie czasu, więc muszę również wydać z instrukcją else. – garlfd

+2

@garlfd: ​​to nadal działa z każdym elementem w łańcuchu, aktualizuje go tylko dla niektórych elementów. –

4

Po pierwsze, można przekształcić łańcuch na iteracyjnej nad wartościami function obie strony:

truths = (function(x) for x in string) 

Następnie można odwzorować te do 0 i 1:

onesandzeroes = (1 if function(x) else 0 for x in string) 

A potem accumulate nich

running = itertools.accumulate(1 if function(x) else 0 for x in string) 

W notatce doktora dodano accumulate w Python 3.2. Jeśli używasz wersji 2.x, możesz skopiować i wkleić przepis "równoważny z" z dokumentacji. (Jeśli używasz 3.0-3.1, możesz zrobić to samo, ale tak naprawdę, w takim przypadku, po prostu uaktualnij.)

Powiązane problemy