Die Sprache die ich da verwendet habe ist
Haskell.
Deine Funktion ist nicht Endrekursiv. Endrekursiv ist eine Funktion, wenn der rekursive Aufruf die letzte Aktion in der Berechnung der Funktion ist. Das ist bei Dir in beiden Fällen nicht der Fall, weil das Ergebnis des rekursiven Aufrufs noch mit `n` multipliziert werden muss, bevor das Ergebnis feststeht.
Endrekursion ist deshalb wünschenswert, weil nach dem rekursiven Aufruf keine der lokalen Variablen mehr benutzt werden, was bedeutet, dass man sie schon vorher freigeben kann. Oder wenn sich die Funktion selbst aufruft, kann man den aktuellen Stack-Rahmen einfach wiederverwenden, womit die Rekursion vom Compiler effektiv zu einer einfachen Schleife übersetzt wird. Das zielt nur indirekt auf die Laufgeschwindigkeit sondern mehr auf den unnötigen Speicherverbrauch auf dem Stack in diesem Fall.
Das gilt allerdings nur für Compiler die auch Endrekursion entfernen, oder auf Englisch "tail call optimization" durchführen. Das macht CPython nicht automatisch. Es gibt aber einen lustigen "Hack" als Dekorator, den man auf eine Funktion anwenden kann, von der man sicher weiss, dass sie endrekursiv ist:
Tail Call Optimization Decorator. Der "Hack" verändert nicht den Bytecode sondern erkennt den rekursiven Aufruf und löst eine Ausnahme aus bevor die Funktion erneut aufgerufen wird und verhindert so dass der Stack wächst.