it-swarm.com.de

Grundlagen der Rekursion in Python

"Schreiben Sie eine rekursive Funktion," listSum ", die eine Liste von Ganzzahlen verwendet und die Summe aller Ganzzahlen in der Liste zurückgibt." 

Beispiel:

>>>> listSum([1,3,4,5,6])
19

Ich weiß, wie man das anders macht, aber nicht auf rekursive Weise.

def listSum(ls):
    i = 0
    s = 0
    while i < len(ls):
        s = s + ls[i]
        i = i + 1
    print s

Ich brauche den grundlegenden Weg, um dies zu tun, da spezielle integrierte Funktionen nicht zulässig sind.

22
Sebastian S

Wenn Sie mit einem solchen Problem konfrontiert werden, versuchen Sie, das Ergebnis der Funktion mit derselben Funktion auszudrücken.

In Ihrem Fall erhalten Sie das Ergebnis, indem Sie die erste Zahl mit dem Ergebnis des Aufrufs derselben Funktion mit den restlichen Elementen in der Liste hinzufügen.

Zum Beispiel,

listSum([1, 3, 4, 5, 6]) = 1 + listSum([3, 4, 5, 6])
                         = 1 + (3 + listSum([4, 5, 6]))
                         = 1 + (3 + (4 + listSum([5, 6])))
                         = 1 + (3 + (4 + (5 + listSum([6]))))
                         = 1 + (3 + (4 + (5 + (6 + listSum([])))))

Was sollte nun das Ergebnis von listSum([]) sein? Es sollte 0 sein. Das heißt Basisbedingung Ihrer Rekursion. Wenn die Basisbedingung erfüllt ist, endet die Rekursion. Versuchen wir es jetzt zu implementieren.

Die Hauptsache hier ist das Aufteilen der Liste. Sie können dazu Slicing verwenden.

Einfache Version

>>> def listSum(ls):
...     # Base condition
...     if not ls:
...         return 0
...
...     # First element + result of calling `listsum` with rest of the elements
...     return ls[0] + listSum(ls[1:])
>>> 
>>> listSum([1, 3, 4, 5, 6])
19

Tail Call Rekursion

Wenn Sie erst einmal verstanden haben, wie die obige Rekursion funktioniert, können Sie versuchen, es etwas besser zu machen. Um nun das tatsächliche Ergebnis zu finden, sind wir auch vom Wert der vorherigen Funktion abhängig. Die return-Anweisung kann den Wert nicht sofort zurückgeben, bis der rekursive Aufruf ein Ergebnis zurückgibt. Wir können dies vermeiden, indem wir den Strom so an den Funktionsparameter übergeben

>>> def listSum(ls, result):
...     if not ls:
...         return result
...     return listSum(ls[1:], result + ls[0])
... 
>>> listSum([1, 3, 4, 5, 6], 0)
19

Hier übergeben wir den Anfangswert der Summe in den Parametern, der in listSum([1, 3, 4, 5, 6], 0) Null ist. Wenn dann die Basisbedingung erfüllt ist, akkumulieren wir tatsächlich die Summe im result-Parameter und geben sie zurück. Die letzte return-Anweisung hat listSum(ls[1:], result + ls[0]), wobei wir das erste Element zum aktuellen result hinzufügen und es erneut an den rekursiven Aufruf übergeben.

Dies könnte ein guter Zeitpunkt sein, um Tail Call zu verstehen. Es wäre für Python nicht relevant, da es keine Tail Call-Optimierung gibt.


Indexversion weitergeben

Nun denken Sie vielleicht, dass wir so viele Zwischenlisten erstellen. Kann ich das vermeiden?

Natürlich kannst du. Als Nächstes müssen Sie nur den Index des Artikels bearbeiten. Aber jetzt wird die Grundbedingung anders sein. Wie bestimmen wir, wie die gesamte Liste verarbeitet wurde, da wir den Index übergeben werden? Wenn der Index der Länge der Liste entspricht, haben wir alle darin enthaltenen Elemente verarbeitet.

>>> def listSum(ls, index, result):
...     # Base condition
...     if index == len(ls):
...         return result
...
...     # Call with next index and add the current element to result
...     return listSum(ls, index + 1, result + ls[index])
... 
>>> listSum([1, 3, 4, 5, 6], 0, 0)
19

Version der inneren Funktion

Wenn Sie jetzt die Funktionsdefinition betrachten, übergeben Sie ihr drei Parameter. Nehmen wir an, Sie werden diese Funktion als API freigeben. Ist es bequem für die Benutzer, drei Werte zu übergeben, wenn sie tatsächlich die Summe einer Liste finden?

Nee. Was können wir dagegen tun? Wir können eine andere Funktion erstellen, die lokal für die eigentliche listSum-Funktion ist, und wir können alle auf die Implementierung bezogenen Parameter wie folgt übergeben

>>> def listSum(ls):
...
...     def recursion(index, result):
...         if index == len(ls):
...             return result
...         return recursion(index + 1, result + ls[index])
...
...     return recursion(0, 0)
... 
>>> listSum([1, 3, 4, 5, 6])
19

Wenn nun listSum aufgerufen wird, wird nur der Rückgabewert der inneren Funktion recursion zurückgegeben, die die Parameter index und result akzeptiert. Jetzt übergeben wir nur diese Werte, nicht die Benutzer von listSum. Sie müssen nur die zu verarbeitende Liste übergeben.

Wenn Sie die Parameter beachten, übergeben wir in diesem Fall ls nicht an recursion, sondern verwenden sie in diesem Parameter. ls ist aufgrund der Schließungseigenschaft in recursion verfügbar.


Standardparameterversion

Wenn Sie es nun einfach halten möchten, ohne eine innere Funktion zu erstellen, können Sie die Standardparameter wie folgt verwenden

>>> def listSum(ls, index=0, result=0):
...     # Base condition
...     if index == len(ls):
...         return result
...
...     # Call with next index and add the current element to result
...     return listSum(ls, index + 1, result + ls[index])
... 
>>> listSum([1, 3, 4, 5, 6])
19

Wenn der Anrufer keinen Wert explizit übergibt, wird 0 sowohl index als auch result zugewiesen.


Rekursives Stromproblem

Lassen Sie uns jetzt die Ideen auf ein anderes Problem anwenden. Versuchen wir beispielsweise, die Funktion power(base, exponent) zu implementieren. Es würde den Wert von base an die Macht exponent anheben.

power(2, 5) = 32
power(5, 2) = 25
power(3, 4) = 81

Nun, wie können wir das rekursiv machen? Versuchen wir zu verstehen, wie diese Ergebnisse erreicht werden.

power(2, 5) = 2 * 2 * 2 * 2 * 2 = 32
power(5, 2) = 5 * 5             = 25
power(3, 4) = 3 * 3 * 3 * 3     = 81

Hmmm, also bekommen wir die Idee. Die base multipliziert mit sich selbst, exponent-Zeiten ergeben das Ergebnis. Okay, wie gehen wir damit um. Versuchen wir, die Lösung mit derselben Funktion zu definieren.

power(2, 5) = 2 * power(2, 4)
            = 2 * (2 * power(2, 3))
            = 2 * (2 * (2 * power(2, 2)))
            = 2 * (2 * (2 * (2 * power(2, 1))))

Was sollte das Ergebnis sein, wenn etwas an die Macht gebracht wird? Ergebnis wird die gleiche Nummer sein, oder? Wir haben unsere Grundbedingung für unsere Rekursion erhalten :-)

            = 2 * (2 * (2 * (2 * 2)))
            = 2 * (2 * (2 * 4))
            = 2 * (2 * 8)
            = 2 * 16
            = 32

Okay, lass es implementieren.

>>> def power(base, exponent):
...     # Base condition, if `exponent` is lesser than or equal to 1, return `base`
...     if exponent <= 1:
...         return base
...
...     return base * power(base, exponent - 1)
... 
>>> power(2, 5)
32
>>> power(5, 2)
25
>>> power(3, 4)
81

Okay, wie wird die optimierte Version von Tail Call definiert? Das aktuelle Ergebnis übergeben wir als Parameter an die Funktion selbst und geben das Ergebnis zurück, wenn die Basisbedingung erfüllt ist. Lassen Sie uns einfach bleiben und verwenden Sie den Standardparameteransatz direkt.

>>> def power(base, exponent, result=1):
...     # Since we start with `1`, base condition would be exponent reaching 0
...     if exponent <= 0:
...         return result
...
...     return power(base, exponent - 1, result * base)
... 
>>> power(2, 5)
32
>>> power(5, 2)
25
>>> power(3, 4)
81

Jetzt reduzieren wir den exponent-Wert in jedem rekursiven Aufruf und in mehreren result mit base und übergeben ihn an den rekursiven power-Aufruf. Wir beginnen mit dem Wert 1, da wir uns dem Problem umgekehrt nähern. Die Rekursion wird so geschehen

power(2, 5, 1) = power(2, 4, 1 * 2)
               = power(2, 4, 2)
               = power(2, 3, 2 * 2)
               = power(2, 3, 4)
               = power(2, 2, 4 * 2)
               = power(2, 2, 8)
               = power(2, 1, 8 * 2)
               = power(2, 1, 16)
               = power(2, 0, 16 * 2)
               = power(2, 0, 32)

Da exponent Null wird, ist die Basisbedingung erfüllt und result wird zurückgegeben, sodass wir 32 :-) erhalten.

78
thefourtheye

Das frühe Beenden ist typisch für rekursive Funktionen. seq ist im leeren Zustand falsch (wenn also keine Zahlen mehr zu summieren sind).

Mit der Slice-Syntax können Sie die Sequenz an eine rekursiv aufgerufene Funktion übergeben, ohne dass im aktuellen Schritt eine Ganzzahl verbraucht wird.

def listSum(seq):
    if not seq:
        return 0
    return seq[0] + listSum(seq[1:])

print listSum([1,3,4,5,6])  # prints 19
3
def power(a,b): #a^b
    if b==0:
        return 1
    Elif b>0:
        return a * power(a,b-1)
    Elif b<0:
        return power(a, b+1)/a
0
Anonymous
def listsum(list):
    if len(list) == 1:
        return list[0]
    else:
        return list[0] + listsum(list[1:])

print(listsum([1,5,9,10,20]))

Die Grundidee dieser rekursiven Funktion ist, dass wir prüfen wollen, ob wir einen Basisfall haben, der als if len(list) == 1: angezeigt wird. Für den Basisfall geben wir nur den Wert in der Liste return list[0] zurück, andernfalls haben wir noch mehrere Elemente in der Liste. In der else:-Anweisung fügen wir das erste Element aus der Liste, das list[0] ist, zu den übrigen Elementen in der Liste hinzu. Dies wird angezeigt, wenn die Funktion rekursiv aufgerufen wird. listsum(list[1:]) wiederholt sich dieser Vorgang, wobei die Liste immer kleiner wird, bis Sie im Basisfall angekommen sind - eine Liste mit Länge 1 und Sie erhalten ein Endergebnis.

0
RamJet
def listSum(L):
    """Returns a sum of integers for a list containing
    integers.
    input: list of integers
    output: listSum returns a sum of all the integers
    in L.
    """
    if L == []:
        return []
    if len(L) == 1:
        return L[0]
    else:
        return L[0] + listSum(L[1:])
print listSum([1, 3, 4, 5, 6])
print listSum([])
print listSum([8])
0
Eliza

Andere Version:

def listSum(ls):
    ls_len = len(ls)

    # Base condition
    if ls_len==1:
        return ls[0]
    if ls_len==0:
        return None
    # ls = listSum(ls[0:i]) + listSum(ls[i:])
    Elif ls_len%2==0:
            i = int(ls_len/2)
            return listSum(ls[0:i]) + listSum(ls[i:])
    else:
        i = int((ls_len-1)/2)
        return listSum(ls[0:i]) + listSum(ls[i:])

Folgen Sie @ thefourtheyes Beispiel. Wir können sagen:

listSum([1, 3, 4, 5, 6]) = listSum([1, 3]) + listSum([4, 5, 6])
                         = (listSum([1]) + listSum([3])) + (listSum([4]) + listSum([5, 6]))
                         = (listSum([1]) + listSum([3])) + (listSum([4]) + (listSum([5]) + listSum([6])))

Basisbedingung: Wenn ls nur ein Element hat, geben Sie diesen Wert zurück.

0
Belter