8

Napisałem prosty skrypt do rozwiązania "łamigłówki logicznej", rodzaj łamigłówki ze szkoły, w której podano wiele zasad, a następnie musi być w stanie znaleźć rozwiązanie problemów takich jak "Jest pięciu muzyków o nazwach A, B , C, D i E grają na koncercie, każdy gra jeden po drugim ... jeśli A ma przed B, a D nie jest ostatni ... Jaka jest kolejność gry? " itpCzy istnieje język Python do oceny listy funkcji/wyrażeń z powodu zwarcia?

Aby ocenić ewentualne rozwiązania, pisałem każdy „zasadę” jako odrębnej funkcji, która będzie ocenić, czy możliwe rozwiązanie (przedstawiciele po prostu jako listę łańcuchów) jest ważny, na przykład

#Fifth slot must be B or D 
def rule1(solution): 
    return solution[4] == 'B' or solution[4] == 'D' 

#There must be at least two spots between A and B 
def rule2(solution): 
    returns abs(solution.index('A') - solution.index('B')) >= 2 

#etc... 

I Interesuje mnie znalezienie Pythonicznego sposobu testowania, czy możliwe rozwiązanie spełnia wszystkie takie reguły, z możliwością zaprzestania oceniania reguł po tym, jak pierwsza się nie powiedzie.

Początkowo napisałem najprostszy z możliwych rzeczy:

def is_valid(solution): 
    return rule1(solution) and rule2(solution) and rule3(solution) and ... 

Ale to wydawało się raczej brzydki. Myślałam, że może mógłbym zrobić to odczytać nieco bardziej elegancki coś jak lista zrozumieniem ...

def is_valid(solution) 
    rules = [rule1, rule2, rule3, rule4, ... ] 
    return all([r(solution) for f in rules]) 

... ale potem zdałem sobie sprawę, że ponieważ lista rozumienie jest generowany przed funkcją all() ocenia, że ma to skutek uboczny, że w ogóle nie ma zwarcia - każda reguła będzie oceniona, nawet jeśli pierwsza zwróci False.

Więc moje pytanie brzmi: czy istnieje bardziej pythonowy/funkcjonalny sposób, aby móc ocenić listę True/False wyrażeń, ze zwarciem, bez konieczności napisać długą listę return f1(s) and f2(s) and f3(s) ...?

Odpowiedz

13

Użyj generator expression:

rules = [ rule1, rule2, rule3, rule4, ... ] 
rules_generator = (r(solution) for r in rules) 
return all(rules_generator) 

cukier syntaktyczna: można pominąć dodatkowych nawiasów:

rules = [ rule1, rule2, rule3, rule4, ... ] 
return all(r(solution) for r in rules) 

Generator jest (zasadniczo) obiekt z metody .next(), która zwraca następny element w niektórych iterable. Oznacza to, że mogą robić użyteczne rzeczy, takie jak czytanie pliku w porcjach bez wczytywania go do pamięci lub iteracji do wielkich liczb całkowitych. Możesz powtórzyć je z przezroczystymi pętlami for; Python obsługuje to za kulisami. Na przykład range jest generatorem w Py3k.

Można toczyć własne wyrażenia niestandardowego generatora za pomocą komunikatu yield zamiast return w definicji funkcji:

def integers(): 
    i = 0 
    while True: 
     yield i 

i Python będzie obsługiwać zapisywania stanu z funkcji i tak dalej. Oni są świetni!

+1

Podstawowa różnica polega na tym, że pomijając nawiasy w 'return all ([r (solution) for r in rules])', nie tworzy się lista wszystkich wyników zanim 'all()' zostanie oceniony? –

+2

Tak. W obu przypadkach argument "all" jest oceniany przed przekazaniem, ale ocena zrozumienia listu tworzy całą listę w pamięci, podczas gdy ocena wyrażenia generatora tworzy obiekt generatora, który ładuje elementy na żądanie. – katrielalex

+0

Doskonały, to ma wiele sensu - dzięki –

Powiązane problemy