2013-03-26 15 views
5

Potrzebuję funkcji Python iterate(f, x), która tworzy iterator zwracający wartości x, f (x), f (f (x)), f (f (f (x))) itp (jak np. Clojure's iterate). Przede wszystkim zastanawiałem się: czy to już istnieje w standardowej bibliotece i brakuje mi jej tylko? Oczywiście jest to dość łatwe do wdrożenia z generatorem:Używanie itertools do aplikacji funkcji rekursywnej

def iterate(f, x): 
    while True: 
     yield x 
     x = f(x) 

Tak z ciekawości: Czy jest bardziej funkcjonalny sposób to zrobić w Pythonie, np z niektórymi magicznymi narzędziami lub magicznymi funkcjami?

W Pythonie 3.3 to będzie działać

def iterate(f, x): 
    return accumulate(repeat(x), lambda acc, _ : f(acc)) 

ale wygląda nadużycia do mnie. Czy mogę to zrobić ładniej?

+3

Powiedziałbym, że 'Akumuluj()' wersja jest po prostu w porządku. * Obie wersje * są w porządku. –

+1

To, co naprawdę dziwne jest w wersji 'accumulate()', to potrzeba 'repeat (x)' lub czegoś podobnego, ponieważ x jest naprawdę potrzebna * raz * jako nasienie do obliczeń. – embee

+0

@embee Teraz, kiedy o tym wspomniałeś, uważam to za dziwne. Najlepiej jako pierwsze rozwiązanie – jamylak

Odpowiedz

4

Wydaje się, że nie ma czegoś w narzędziach, które robią to, co chcesz, ale itertools to głęboka skrzynia skarbów, więc mogłem coś przeoczyć.

Twój kod generatora wygląda świetnie. Nie wiem, dlaczego napisałbyś to z akumulatorką, chyba że grasz w absurdalną grę w golfa kodu lub próbujesz zrobić wrażenie na snobach Haskella. Napisz swoją funkcję, aby była czytelna, zrozumiała i możliwa do utrzymania. Nie trzeba być zbyt sprytnym.

+0

Masz absolutną rację i nigdy bym nie pomyślał o dziwacznej wersji "akumuluj" przed napisaniem tego postu. Naprawdę zastanawiałem się, czy brakowało mi miłego, zwięzłego sposobu na użycie itertools. Przekształcam funkcje w iteratory (jak tutaj lub w przykładach 'tabulate' z [receptur itertools] (http: // docs.python.org/2/library/itertools.html#recipes) od czasu do czasu i zastanawiał się, jaki byłby najbardziej pythonic sposób to zrobić. – embee

3

można użyć anamorphism (lub rozwijać) uproszczenie definicji iterate i używać tylko jedną wartość wyjściową. Poniżej znajduje się realizacja I kiedyś, w oparciu o dość znanym paper:

def ana(build, predicate): 
    def h(x): 
     if predicate(x): 
      return 
     else: 
      a, b = build(x) 
      yield a 
      for i in h(b): 
       yield i 
      # with newer syntax: 
      # yield from h(b) 
    return h 

Wdrażanie iterate z ana wówczas wygląda następująco:

def iterate(f, x): 
    return ana(lambda x: (x, f(x)), lambda _: False)(x) 

Brak itertools, chociaż ... I zgadzam się, że nie jest to najbardziej czytelny wariant. W rzeczywistości jest to raczej zagadkowe.


AKTUALIZACJA: Istnieje łatwiejsza wersja, która wygląda nawet ładnie. Zajęło z here:

def unfold(f, x): 
    while True: 
     w, x = f(x) 
     yield w 

I to daje:

def iterate(f, x): 
    return unfold(lambda y: (y, f(y)), x) 
+0

Podoba mi się ten. Interesujące jest to, że 'unfold' ma taką samą strukturę jak mój oryginalny generator' iterate', ale zyskuje zupełnie nowy poziom abstrakcji i ogólności, ponieważ f zwraca teraz pary zamiast jednej wartości. Miły. – embee

Powiązane problemy