Sie sagen, die Tail-Rekursion-Optimierung funktioniert nur, wenn der Aufruf kurz vor der Rückkehr aus der Funktion ist. So zeigen sie diesen Code als Beispiel dafür, was sollte nicht von C-Compiler optimiert werden:Warum optimiert g ++ immer noch die Tail-Rekursion, wenn das Ergebnis der Rekursionsfunktion multipliziert wird?
long long f(long long n) {
return n > 0 ? f(n - 1) * n : 1;
}
, weil es der rekursive Funktionsaufruf von n
multipliziert wird, was bedeutet, dass die letzte Operation Multiplikation, nicht rekursiven Aufruf. Es ist jedoch ist auch auf -O1
Ebene:
recursion`f:
0x100000930 <+0>: pushq %rbp
0x100000931 <+1>: movq %rsp, %rbp
0x100000934 <+4>: movl $0x1, %eax
0x100000939 <+9>: testq %rdi, %rdi
0x10000093c <+12>: jle 0x10000094e
0x10000093e <+14>: nop
0x100000940 <+16>: imulq %rdi, %rax
0x100000944 <+20>: cmpq $0x1, %rdi
0x100000948 <+24>: leaq -0x1(%rdi), %rdi
0x10000094c <+28>: jg 0x100000940
0x10000094e <+30>: popq %rbp
0x10000094f <+31>: retq
Sie sagen, dass:
Ihre endgültige Regeln sind daher ausreichend zu korrigieren. Rückgabe
n * fact(n - 1)
hat jedoch eine Operation in der Endposition! Dies ist die Multiplikation*
, die das letzte ist, was die Funktion tut, bevor sie zurückkehrt. In einigen Sprachen könnte dies tatsächlich als Funktionsaufruf implementiert sein, der dann als Tail-Call optimiert werden könnte.
Wie wir jedoch aus der ASM-Auflistung sehen, ist die Multiplikation immer noch ein ASM-Befehl, keine separate Funktion. So kämpfe ich wirklich Unterschied mit Akku Ansatz zu sehen:
int fac_times (int n, int acc) {
return (n == 0) ? acc : fac_times(n - 1, acc * n);
}
int factorial (int n) {
return fac_times(n, 1);
}
Dies erzeugt
recursion`fac_times:
0x1000008e0 <+0>: pushq %rbp
0x1000008e1 <+1>: movq %rsp, %rbp
0x1000008e4 <+4>: testl %edi, %edi
0x1000008e6 <+6>: je 0x1000008f7
0x1000008e8 <+8>: nopl (%rax,%rax)
0x1000008f0 <+16>: imull %edi, %esi
0x1000008f3 <+19>: decl %edi
0x1000008f5 <+21>: jne 0x1000008f0
0x1000008f7 <+23>: movl %esi, %eax
0x1000008f9 <+25>: popq %rbp
0x1000008fa <+26>: retq
ich etwas fehlt? Oder sind Compiler nur schlauer geworden?
Wer auch immer "sie" sind, sie liegen falsch. – nos
Natürlich ist 'f (n-1) * n' 'n * f (n-1)'. Ich sehe nicht, dass das eine Überraschung ist. – MSalters
gcc ist schlauer als "sie" –