2013-03-26 10 views
5

Ich brauche eine Python-Funktion iterate(f, x), die einen Iterator erstellt, der die Werte x, f (x), f (f (x)), f (f (f (x))) usw. zurückgibt (wie zB Clojure's iterate). Zuallererst fragte ich mich: Gibt es das schon irgendwo in der Standardbibliothek und ich vermisse es nur? Natürlich ist es einfach genug, um mit einem Generator zu implementieren:Verwenden von itertools für rekursive Funktion Anwendung

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

Nur aus Neugier: Gibt es eine funktionale Möglichkeit, dies in Python zu tun, zum Beispiel mit einigen itertools oder functools magie?

In Python 3.3 würde dies

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

arbeiten, aber sieht aus wie ein Missbrauch zu mir. Kann ich das schöner machen?

+3

Ich würde sagen, die 'accumulate()' Version nur in Ordnung ist. * Beide * Versionen sind in Ordnung. –

+1

Was ich wirklich seltsam finde an der 'accumulate()' Version ist die Notwendigkeit für 'repeat (x)' oder etwas Ähnliches, weil x nur * einmal * als Grund für die Berechnung benötigt wird. – embee

+0

@embee Jetzt, wo du es erwähnst, finde ich das auch komisch. Prob als die erste Lösung – jamylak

Antwort

4

Es scheint nicht etwas in iertools zu sein, das tut, was du willst, aber itertools ist eine tiefe Schatzkiste, also hätte ich etwas übersehen können.

Ihr Generatorcode sieht gut aus. Ich weiß nicht, warum du es mit accumulate schreibst, außer du spielst ein absurdes Spiel mit Code-Golf, oder du hast versucht, Haskell Snobs zu beeindrucken. Schreiben Sie Ihre Funktion so, dass sie lesbar, verständlich und wartbar ist. Kein Grund, übermäßig schlau zu sein.

+0

Sie haben absolut Recht und ich hätte nie über die bizarre'Akkumulation'-Version nachdenken können, bevor ich diesen Beitrag geschrieben habe. Ich habe mich wirklich gefragt, ob ich eine nette, prägnante Art und Weise vermisste, Itertools zu benutzen. Ich stelle fest, dass ich Funktionen in Iteratoren umwandle (wie hier oder im 'tabulate' Beispiel aus den [itertools recipes] (http: // docs.python.org/2/library/itertools.html#recipes) von Zeit zu Zeit und fragte sich, was der pythischste Weg wäre, es zu tun. – embee

3

können Sie verwenden ein anamorphism (oder entfalten) die Definition von iterate, zu vereinfachen und nur einen Ausgangswert zu verwenden. Hier ist eine Implementierung ich einmal verwendet, basiert auf einem recht bekannten 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 

Implementierung iterate mit ana dann wie folgt aussieht:

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

Keine itertools, obwohl ... Und ich stimme zu, dass Dies ist nicht die lesbarste Variante. In der Tat ist es eher kryptisch.


UPDATE: Es gibt eine einfachere Version, die sogar ganz nett aussieht. Es ist aus here genommen:

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

Und das gibt Ihnen:

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

Ich mag diesen. Interessanterweise ist dieses "Entfalten" genau so aufgebaut wie mein ursprünglicher "Iterate" -Generator, erhält aber eine völlig neue Ebene der Abstraktion und Allgemeingültigkeit, weil f nun Paare statt eines einzelnen Wertes zurückgibt. Nett. – embee