Iterative und Rekursive Klassen bei OOP

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Marceline
User
Beiträge: 20
Registriert: Freitag 2. November 2018, 16:35

Schreibe ein Python 3.7.x Programm, welches folgende Anforderungen erfüllt.

Bedingung: Die Lösung erfolgt komplett objektorientiert.

In einer Klasse wird die Fibonacci-Folge sowohl iterativ, als auch rekursiv berechnet.

In einer anderen Klasse wird die Fakultät sowohl iterativ, als auch rekursiv berechnet.

Mittels der Vererbung bekommt eine dritte (Unter-) Klasse die Eigenschaften und Methoden der beiden oben genannten Klassen.

Über die Konsole ist eine positive ganze Zahl von einer Instanz der Unterklasse entgegenzunehmen. Ist diese Zahl gerade, ist die Fibonacci-Zahl mit diesem Index rekursiv zu berechnen. Ist diese Zahl ungerade, ist die Fakultät iterativ zu berechnen.

Schreibe eine Funktion noise_me() in der Unterklasse, welche das Ergebnis der oben beschriebenen Berechnung entgegennimmt und einen Durchschnittswert D der Ziffern dieser Zahl berechnet und gegebenenfalls D aufrundet.

Für eine gerade Zahl D wird zufällig rekursiv oder iterativ die Fibonacci-Zahl an der jeweiligen Stelle berechnet. Das Ergebnis wird an die Funktion noise_me() übergeben.

Für eine ungerade Zahl D wird zufällig rekursiv oder iterativ die Fakultät berechnet. Das Ergebnis wird an die Funktion noise_me() übergeben. Nach 1000 Aufrufen der Funktion noise_me() geben Sie in der Konsole aus, wie oft Ihre jeweilige rekursive und iterative Implementierung der Berechnung der Fibonacci-Zahl und der Fakultät (4 Werte) aufgerufen wurde.


Problem: Ich kann mir unter OOP bei Fibonacci und Fakultät wenig bis gar nichts vorstellen. Normalerweise werden solche Klassen ja erzeugt um Attribute und Werte aufzurufen und einzusehen. Bei Fibonacci muss doch gar nichts in der Shell definiert oder verändert werden. Ein erster Versuch meinerseits zur Implementierung, scheiterte, wie erwartet, kläglich.

Code: Alles auswählen


####rekursiv


class Fibonacci:

    
    def nthNumber (self, n, recursive):
        if recursive:
            return self.nthNumber_recursive(n)
        else:
            return self.nthnNumber_iterative (n)
    

    def nthNumber_recursive(self,n):
        #self.n =+1
        if n == 1:
            return 1
        elif n == 2:
            return 1
        elif n > 2:
            return fibonacci(n-1) + fibonacci (n-2)
            #self.result = self.recursive (value)

    for n in range (1,10):
        print (n, ".", fibonacci(n))

    def nthNumber_iterative(self,n):
    #### iterativ
#def fibonacci(n):
        if n == 1:
            return 1
        elif n == 2:
            return 1
        a, b = 0, 1
        for n in range(0, n):
            a, b = b, a + b
        return a

for n in range (0,10):
    print (n, ":", fibonacci(n))
            #self.result = self.iterative (value)
Angeblich ist "fibonacci" nüscht definiert worden. Aber ich definiere doch Fibonacci = [self.a, self.b]?
Benutzeravatar
__blackjack__
User
Beiträge: 14032
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marceline: Die Aufgabenstellung ist total Grütze weil da nichts objektorientiert ist. Mir scheint der Aufgabensteller ist der Ansicht das man automatisch OOP betreibt wenn man nur alles in Klassen stopft. Also auch Funktionen. Das stimmt aber nicht. Das bleibt Funktional, bloss halt unsinnigerweise in Klassen gesteckt.

`fibonacci` ist in dem gezeigten Quelltext in der Tat nirgends definiert worden. Und auch `Fibonacci` ist nirgends als ``[self.a, self.b]`` definiert wie Du das im Beitragstext behauptest. Würde auch keinen Sinn machen.

Die erste ”Methode” in Deiner `Fibonacci`-Klasse ist nirgends gefordert, die würde ich weglassen. Wenn der Aufrufer einen Wahrheitswert mitgeben kann um zu entscheiden welche ”Methode” tatsächlich ausgeführt werden soll, dann kann der auch gleich die tatsächlich gewünschte ”Methode” aufrufen.

den `nthNumber_*`-Präfix bei den Namen würde ich weglassen oder zumindest so schreiben wie es in Python üblich ist: klein_mit_unterstrichen.

Wenn es keine echte Methode ist und die bekloppte Aufgabenstellung Dich zwingt die Funktionen in eine Klasse zu stecken, würde ich zumindest mit einem `staticmethod`-Dekorator deutlich machen dass Du *weisst*, dass das keine echten Methoden sind.

Die ``for``-Schleife im Klassenkörper hat da nichts zu suchen.

Den Startwert 0 braucht man bei `range()` nicht angeben.

Die ganzen Kommentare können weg. Die bringen entweder keinen Gewinn für den Leser oder sie verwirren einfach nur.

Fang einfach mal mit Funktionen an. Erst Fibonacci rekursiv, dann testen ob die funktioniert. Wenn sie das tut, dann Fibonacci iterativ. Testen ob sie funktioniert und die gleichen Ergebnisse liefert wie die rekursive Variante. Wenn sie das tut, das gleiche mit der Fakultät. Und auch da erst wieder die eine, und erst wenn die nachgewiesen funktioniert, die andere.

*Dann* kannst Du die jeweils in eine unnötige Klasse verschieben, mit `staticmethod` dekorieren, und die dritte Klasse schreiben die von beiden erbt. (An der Stelle kämpfe ich so ein bisschen mit Brechreiz :twisted:)

Und dort dann die `noise_me()`-”Methode”. (Kann man dem Aufgabensteller vielleicht irgendwie die Lehrerlaubnis entziehen? Das ist ja schon fast bösartig so etwas unter dem Etikett „wir lernen OOP“ zu machen…)
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
__blackjack__
User
Beiträge: 14032
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ach so, bei der `noise_me()`-Funktion sollte man vielleicht noch ausdrücklich vor der `round()`-Funktion warnen. Die ist nicht für die Aufgabe geeignet!
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
Marceline
User
Beiträge: 20
Registriert: Freitag 2. November 2018, 16:35

Hi, danke für deine Antwort, blackjack.
__blackjack__ hat geschrieben: Freitag 25. Januar 2019, 22:54Fang einfach mal mit Funktionen an. Erst Fibonacci rekursiv, dann testen ob die funktioniert. Wenn sie das tut, dann Fibonacci iterativ. Testen ob sie funktioniert und die gleichen Ergebnisse liefert wie die rekursive Variante. Wenn sie das tut, das gleiche mit der Fakultät. Und auch da erst wieder die eine, und erst wenn die nachgewiesen funktioniert, die andere.

*Dann* kannst Du die jeweils in eine unnötige Klasse verschieben, mit `staticmethod` dekorieren, und die dritte Klasse schreiben die von beiden erbt. (An der Stelle kämpfe ich so ein bisschen mit Brechreiz :twisted:)
Also Fibonacci hab ich probiert mit normalen Funktionen. Die scheinen auch einwandfrei zu laufen.

Code: Alles auswählen

####rekursiv

def fibonacci (n):  #n-turn
    if n == 1:
        return 1
    elif n == 2:
        return 1
    elif n > 2:
        return fibonacci(n-1) + fibonacci (n-2)

for n in range (1,10):
    print (n, ".", fibonacci(n))
    
#### iterativ
def fibonacci(n):
    if n == 1:
        return 1
    elif n == 2:
        return 1
    a, b = 0, 1
    for n in range(0, n):
        a, b = b, a + b
    return a

for n in range (0,10):
    print (n, ":", fibonacci(n))
Bei Fakultät habe ich es mit Klassen probiert, das Programm scheint auch zu laufen. Allerdings verstehe ich nicht, wie ich das jetzt objektorientiert anwenden kann. Wenn ich z.B. die Variable 4 anlege und dann die Fakultät von 4 erzeugen will (4 = fact (4)) erscheint "can't assign to literal"

Code: Alles auswählen

class Fact():
    def __init__(value, b):
        if b == True:
            self.result = self.recursive(value)
        else:
            self.result = self.iterative(value)
        print (faculty)


def rekursive (x):
    print (x)
    if (x > 0):
        rekursiv(x-1)
    else:
        return

def iterative (x):
    while x >= 0:
        print (x)
        x= x - 1

def fact(x):
    if x == 1 or x == 0:
        return 1
    else:
        return x * fakt(x-1)

def fact2(x):
    y = 1
    while x > 0:
        y = x * y
        x = x - 1
    return y

Die Kommentare mit Code-Schnipsel schreib ich meistens, wenn ich eine Idee zu etwas habe, sie aber nicht implementieren kann. Bei uns ist es leider so, dass das Programm lauffähig sein muss, ansonsten wird die komplette Aufgabe mit 0 Punkten bewerten. Heißt: Wenn ich irgendwo einen Syntaxfehler oder sonstigen Fehler habe, den ich nicht beheben kann, muss ich all das, was diesen Fehler erzeugt, rausschneiden, um den Code überhaupt läuffähig und bewertbar zu machen. Daher schreib ich meist Pseudo-Code in die Kommentare, weil es mir irgendwie immer ein bisschen "weh tut", die entwickelten Ideen wieder zu löschen.
Benutzeravatar
Marceline
User
Beiträge: 20
Registriert: Freitag 2. November 2018, 16:35

Da ich irgendwie zu blöd bin hier die Funktion zum nachträglichen Editieren von Beiträgen zu finden. Ich habe den Code mittlerweile aktualisiert. Allerdings noch keinen Weg gefunden, eine Vererbung innerhalb von Klassen zu initialisieren, geschweige denn noise_me zu implementieren.

Code: Alles auswählen

####rekursiv


class Fibonacci:
    
    def number (self, n, recursive):
        if recursive:
            return self.number_recursive(n)
        else:
            return self.number_iterative (n)

        self.fibonacci = [self.x,self.y]

    def number_recursive(self,n):
        #self.n =+1
        if n == 1:
            return 1
        elif n == 2:
            return 1
        elif n > 2:
            return number_recursive(n-1) + number_recursive (n-2)
            #self.result = self.recursive (value)

    for n in range (1,10):
        print (n, ".", number_recursive)

    def number_iterative(self,n):
    #### iterativ
    #def fibonacci(n):
        if n == 1:
            return 1
        elif n == 2:
            return 1
        a, b = 0, 1
        for n in range(0, n):
            a, b = b, a + b
        return a

    for n in range (0,10):
        print (n, ":", number_iterative)
            #self.result = self.iterative (value)

####################################
class Fact():
    def __init__(value, b):
        if b == True:
            self.result = self.recursive(value)
        else:
            self.result = self.iterative(value)
        print (faculty)


def rekursive (x):
    print (x)
    if (x > 0):
        rekursiv(x-1)
    else:
        return

def iterative (x):
    while x >= 0:
        print (x)
        x= x - 1

def fact(x):
    if x == 1 or x == 0:
        return 1
    else:
        return x * fakt(x-1)

def fact2(x):
    y = 1
    while x > 0:
        y = x * y
        x = x - 1
    return y
 

    

def main():
    """ This programm models a inheritance between fibonacci number
    and the faculty function
    """

    
if __name__ == '__main__':
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14032
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marceline: Vorsicht: Dieser Beitrag bezieht sich noch auf Deinen vorletzten Beitrag!

Im ersten Codeschnippsel hast Du jetzt zweimal `fibonacci()` definiert, aber Du brauchst später ja *beide* also dürfen die nicht gleich heissen. Das wäre Dir aufgefallen wenn Du nicht auf Modulebene einfach so ``for``-Schleifen geschrieben hättest, die dort nicht hingehören.

Die Kommentare bräuchtest Du dann auch nicht mehr, denn das ist dann ja eine Information die in den Namen gehört, damit man die beiden Funktionen auseinanderhalten kann.

Und dann könntest Du die beiden Funktionen als ”Methoden” in eine Klasse stecken. Die Klasse kann dann später noch bedingt sinnvoll werden wenn man sich ganz am Ende anschaut was die Ausgabe des Programms sein soll. Die Vererbung die da gefordert wird ist aber immer noch unsinnig. Da wäre ich beispielsweise an einem sinnvollen Klassennamen interessiert, denn mir fällt da nix ein. Ist ja auch keiner vorgegeben.

Beim zweiten Codeschnippsel wird es wieder recht wirr und Du hast Dich offensichtlich nicht an den Tipp gehalten nicht weiter zu machen bis etwas läuft.

Die Klasse macht keinen Sinn und die `__init__()` führt zu eine `AttributeError`.

Die `rekursive()`-Funktion macht keinen Sinn führt zu einem `NameError`.

Die `iterative()`-Funktion ”funktioniert”, macht aber nichts sinnvolles.

Die `fact()`-Funktion führt wieder zu einem `NameError`.

Und mit `fact2()` haben wir dann tatsächlich eine Funktion die tatsächlich etwas sinnvolles macht. Das ist aber keine wirklich gute Ausbeute.

Bei `fact2()` müsste man vielleicht noch sagen das man hier eine ``for``-Schleife statt einer ``while``-Schleife verwendet würde.

Was Du mit ``4 = fact (4)`` bezwecken wolltest ist mir absolut schleierhaft. Das sollte doch eigentlich klar sein das man 4 keinen anderen Wert zuordnen kann. Was sollte denn anch dieser Zuweisung ``print(4)`` ausgeben? Den Wert 4? Wo ist dann das Ergebnis des `fact()`-Aufrufs geblieben? Oder etwa 24? Wie kann man ab da dann an den Wert 4 heran kommen wenn 4 nun den Wert 24 hat? Und Du merkst schon was für ein Chaos das geben würde wenn man den Wert von Zahlen umdefinieren könnte‽ Dann ist 1+1 nicht mehr unbedingt 2. Argh!
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
__blackjack__
User
Beiträge: 14032
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marceline: Nun zu Deinem letzten Beitrag.

Der erste Kommentar in dem Code macht keinen Sinn, der bezieht sich ja irgendwie auch auf nichts. Die Klasse ist jedenfalls nicht rekursiv.

Die Namen der ”Methoden” wirst Du noch einmal ändern müssen, denn die beiden Klassen sollen ja in einer dritten durch Vererbung zusammengeführt werden und dann müssen die ”Methoden” in beiden Klassen unterschiedlich heissen, denn sonst klappt das ja nicht.

Den Sinn der ersten ”Methode” `number()` sehe ich immer noch nicht. Die ist so nicht gefordert, und solange Du die nicht für irgend eine andere Methode oder Funktion brauchst, lass sie weg. Auf jeden Fall solltest Du den toten Code am Ende entfernen der ja nie erreicht wird, und falls er ausgeführt werden würde, sowieso nur in einem `AttributeError` enden würde.

`number_recursive()` liefert implizit `None` wenn `n` kleiner 0 ist. Das man bei ``if``/``elif`` einen nicht abgedeckten Fall hat, ist immer etwas unschön weil der Leser dann nicht weiss ob das Absicht ist, oder ob das Programm an der Stelle noch nicht fertig ist.

Diese ”Methode” funktioniert auch nicht was das Programm dann ja auch schon mit einem `NameError` kurz nach der Definition der ”Methode” abbrechen lässt.

Die ``for``-Schleifen im Klassenkörper haben da nichts zu suchen.

Die ganzen Kommentare in der Klassen sollten da raus. Falls Du da jetzt wieder mit auskommentierten Ideen argumentieren möchtest: Du möchtest nicht das die jemand sieht, die sind nämlich Unsinn.

Zum Rest habe ich ja schon in meinem letzten Beitrag etwas gesagt.

„Faculty“ ist lustig. Die *mathematische* Fakultät heisst im englischen „factorial“. Wissen viele nicht, weil das so gerne mit `fact` oder gar nur `fac` abgekürzt wird. Schönes Beispiel warum Abkürzungen doof sind. :-)
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Benutzeravatar
ThomasL
User
Beiträge: 1378
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

@Marceline
Wie bereits geschrieben ist die Aufgabenstellung und der didaktische Sinn mehr als fragwürdig und an einigen Stellen imho undeutlich ausformuliert.
Dies wäre meine Umsetzung, stelle ich mal so als Entwurf in den Raum, die Kollegen hier können bestimmt noch Verbesserungen beisteuern.
Ich kann dir nur empfehlen, dies nicht einfach zu kopieren und als dein Werk auszugeben, ohne es verstanden zu haben.
VG, Thomas

Code: Alles auswählen

from random import choice

# In einer Klasse wird die Fibonacci-Folge sowohl iterativ, als auch rekursiv berechnet
class Fibonacci:
    
    def __init__(self):
        self.fib_iter_count = 0
        self.fib_rec_count = 0
    
    def fib_iter(self, n):
        self.fib_iter_count += 1
        if n <= 1:
            return n
        a, b = 0, 1
        for _ in range(n):
            a, b = b, a + b
        return a
    
    def fib_rec(self, n):
        self.fib_rec_count += 1
        if n < 2:
            return n
        return self.fib_rec(n-1) + self.fib_rec(n-2)


# In einer anderen Klasse wird die Fakultät sowohl iterativ, als auch rekursiv berechnet
class Factorial:
    
    def __init__(self):
        self.fac_iter_count = 0
        self.fac_rec_count = 0
    
    def fac_iter(self, n):
        self.fac_iter_count += 1
        factorial = 1
        while n > 1:
            factorial *= n
            n -= 1
        return factorial
    
    def fac_rec(self, n):
        self.fac_rec_count += 1
        if n > 1:
            return n * self.fac_rec(n-1)
        return 1


# Mittels der Vererbung bekommt eine dritte (Unter-) Klasse die Eigenschaften und Methoden der beiden oben genannten Klassen
class Subclass(Fibonacci, Factorial):
    
    def __init__(self, number):
        self.noise_count = 0
        Fibonacci.__init__(self)
        Factorial.__init__(self)
        # Über die Konsole ist eine positive ganze Zahl von einer Instanz der Unterklasse entgegenzunehmen.
        # Ist diese Zahl gerade, ist die Fibonacci-Zahl mit diesem Index rekursiv zu berechnen.
        # Ist diese Zahl ungerade, ist die Fakultät iterativ zu berechnen.
        if number % 2 == 0:
            self.noise_me(self.fib_rec(number))
        else:
            self.noise_me(self.fac_rec(number))
        # Nach 1000 Aufrufen der Funktion noise_me() geben Sie in der Konsole aus, wie oft Ihre jeweilige
        # rekursive und iterative Implementierung der Berechnung der Fibonacci-Zahl und der Fakultät (4 Werte) aufgerufen wurde. 
        print(f'Fibonacci: {self.fib_rec_count}x rekursiv - {self.fib_iter_count}x iterativ')
        print(f'Fakultät: {self.fac_rec_count}x rekursiv - {self.fac_iter_count}x iterativ')

    # Schreibe eine Funktion noise_me() in der Unterklasse, welche das Ergebnis der oben beschriebenen Berechnung entgegen
    # nimmt und einen Durchschnittswert D der Ziffern dieser Zahl berechnet und gegebenenfalls D aufrundet.
    def noise_me(self, number):
        if self.noise_count < 1000:
            self.noise_count += 1
            digits = str(number)
            D = int((sum([int(digit) for digit in digits]) / len(digits)) + 0.5)
            if D % 2 == 0:
                # Für eine gerade Zahl D wird zufällig rekursiv oder iterativ die Fibonacci-Zahl an der jeweiligen Stelle berechnet.
                result = choice([self.fib_iter, self.fib_rec])(D)
            else:
                # Für eine ungerade Zahl D wird zufällig rekursiv oder iterativ die Fakultät berechnet.
                result = choice([self.fac_iter, self.fac_rec])(D)
                
            # Das Ergebnis wird an die Funktion noise_me() übergeben
            self.noise_me(result)


def main():
    # Über die Konsole ist eine positive ganze Zahl von einer Instanz der Unterklasse entgegenzunehmen.
    # zahl = int(input('Zahl eingeben:'))
    # Habe ich mal auskommentiert um zwei konkrete Beispiele zu nennen
    
    number = 40 # aufgrund von Fibonacci rekursiv dauert das bei größeren Zahlen ewig
    subclass = Subclass(number)
    # Ausgabe
    # Fibonacci: 331160309x rekursiv - 1x iterativ
    # Fakultät: 510x rekursiv - 489x iterativ

    # Es können größere ungerade als gerade Zahlen aufgerufen werden, da dort zuerst die Fakultät
    # iterativ berechnet wird, bin jedoch auf dieses interessante Verhalten gestossen.
    number = 2955  # das klappt
    # number = 2957    # RecursionError: maximum recursion depth exceeded in comparison
    subclass = Subclass(number)
    # Ausgabe
    # Fibonacci: 67x rekursiv - 3x iterativ
    # Fakultät: 3475x rekursiv - 476x iterativ


if __name__ == '__main__':
    main()

Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
Marceline
User
Beiträge: 20
Registriert: Freitag 2. November 2018, 16:35

Die Abgabefrist ist leider schon um, aber trotzdem danke für die Antworten und Thomas, danke für die Mühe, das alles zu programmieren.
Da wir die Aufgaben leider nicht besprechen und auch keine Musterlösungen bekommen, kann ich somit zumindest nachvollziehen, was hier gefordert war. :)
Benutzeravatar
__blackjack__
User
Beiträge: 14032
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@ThomasL: Das ist wahrscheinlich eine der Ungenauigkeiten in der Aufgabe, aber ich hab das nicht so verstanden, dass man das Ergebnis von dem ersten Aufruf den Du in der `__init__()` hast, einfach verwirft, das also nur wegen des Nebeneffektes des Aufrufe zählens macht.

@Marceline: Die Aufgabenstellung(en) sind schon so besch…eiden und dann gibt es weder Nachbesprechung noch Musterlösung? Wie soll man da denn überhaupt etwas lernen? Das wäre bei uns ein Fall für die Fachschaftsinitiative gewesen, oder wie auch immer bei euch halt die ”Lernendenvetretung” organisiert ist.

Hier mal mein Ansatz ohne Klassen:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import reduce, wraps
from operator import mul
import random 


def is_even(n):
    return n % 2 == 0


def calls_counted(function):
    """"Wraps a callable in an object that has an attribute counting the
    calls made.
    
    >>> @calls_counted
    ... def greet(name):
    ...     print(f'Hello, {name}!')
    >>> greet.call_count
    0
    >>> greet('World')
    Hello, World!
    >>> greet.call_count
    1
    >>> greet('Alice')
    Hello, Alice!
    >>> greet.call_count
    2
    """
    @wraps(function)
    def wrapper(*args, **kwargs):
        wrapper.call_count += 1
        return function(*args, **kwargs)
        
    wrapper.call_count = 0
    return wrapper


@calls_counted
def fibonacci_recursive(n):
    """Calculate the n-th Fibonacci number recursivly.
    
    >>> fibonacci_recursive(10)
    89
    >>> fibonacci_recursive.call_count
    177
    >>> [fibonacci_recursive(n) for n in range(10)]
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    """
    if n < 2:
        return max(n, 1)
    else:
        return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)


@calls_counted
def fibonacci_iterative(n):
    """Calculate the n-th Fibonacci number iteratively.
    
    >>> fibonacci_iterative(10)
    89
    >>> fibonacci_iterative.call_count
    1
    >>> [fibonacci_iterative(n) for n in range(10)]
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    """
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return b


@calls_counted
def factorial_recursive(n):
    """Calculate n! recursivly.
    
    >>> factorial_recursive(10)
    3628800
    >>> factorial_recursive.call_count
    11
    >>> [factorial_recursive(n) for n in range(10)]
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    """
    return 1 if n <= 0 else n * factorial_recursive(n - 1)


@calls_counted
def factorial_iterative(n):
    """Calculate n! iteratively.

    >>> factorial_iterative(10)
    3628800
    >>> factorial_iterative.call_count
    1
    >>> [factorial_iterative(n) for n in range(10)]
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    """
    return reduce(mul, range(1, n + 1), 1)


@calls_counted
def noise_me(n):
    """Calculate the mean value of the digits of `n` and round the result.
    
    >>> noise_me(10)
    1
    >>> noise_me(4711)
    3
    >>> max(map(noise_me, range(1_000_000)))
    9
    >>> noise_me.call_count
    1000002
    """
    digits = str(n)
    mean = sum(map(int, digits)) / len(digits)
    # 
    # Assuming the assignment text means by „rounding up if necessary“ the
    # rounding mode most of us learned in school where numbers with a fractional
    # part ≥0.5 are rounded up.  So we can not use the built-in :func:`round()`
    # function here.
    # 
    return int(mean + 0.5)


def make_random_caller(functions):
    """Make a function randomly selecting and forwarding the arguments at each
    call.
    
    >>> import random
    >>> random.seed(42)
    >>> def greet(name):
    ...     print(f'Hello, {name}!')
    >>> def cowboy_greet(name):
    ...     print(f'Howdy, {name}!')
    >>> random_greet = make_random_caller([greet, cowboy_greet])
    >>> random_greet('Eve')
    Hello, Eve!
    >>> random_greet('Alice')
    Hello, Alice!
    >>> random_greet('Bob')
    Howdy, Bob!
    """
    return lambda *args, **kwargs: random.choice(functions)(*args, **kwargs)


def ask_positive_integer():
    while True:
        try:
            result = int(input('Please enter a positive integer value: '))
        except ValueError:
            print('Error: That was not an integer value!')
        else:
            if result < 0:
                print('Error: The value must be positive!')
            else:
                return result


def main():
    fibonacci_random = make_random_caller(
        [fibonacci_recursive, fibonacci_iterative]
    )
    factorial_random = make_random_caller(
        [factorial_recursive, factorial_iterative]
    )
    n = ask_positive_integer()
    n = (fibonacci_recursive if is_even(n) else factorial_iterative)(n)
    n = noise_me(n)
    while noise_me.call_count < 1000:
        n = noise_me((fibonacci_random if is_even(n) else factorial_random)(n))

    for function in [
        fibonacci_recursive, fibonacci_iterative,
        factorial_recursive, factorial_iterative,
    ]:
        print(f'{function.__name__} called {function.call_count} times.')


if __name__ == '__main__':
    main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Antworten