为什么应该使用迭代而不是尾部递归
本文关键字:尾部 递归 迭代 为什么 | 更新日期: 2023-09-27 18:28:46
递归中的设计气味和糟糕的实践是什么?当我看到resharper建议改进时,我很快在谷歌上四处查看。看到许多关于将尾部递归重新分解为迭代并将其称为设计气味的评论。
public static void DebugOutput2(Exception ex) {
if (ex == null) {
return;
}
Debug.WriteLine(ex.Message);
if (ex.InnerException != null) {
DebugOutput2(ex.InnerException);
}
}
// WAS REFACTORED TO
public static void DebugOutput(Exception ex) {
if (ex == null) {
return;
}
while (true) {
Debug.WriteLine(ex.Message);
if (ex.InnerException != null) {
ex = ex.InnerException;
continue;
}
break;
}
}
EDIT:B基于C#编译器处理注释。看起来它现在是递归的
目标净4.5.C#5.0
尾递归版本的ILDASM输出:显示递归调用而非迭代
.method public hidebysig static void DebugOutput(class [mscorlib]System.Exception ex) cil managed
{
// Code size 54 (0x36)
.maxstack 2
.locals init ([0] bool CS$4$0000)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldnull
IL_0003: ceq
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: brtrue.s IL_000e
IL_000c: br.s IL_0035
IL_000e: ldarg.0
IL_000f: callvirt instance string [mscorlib]System.Exception::get_Message()
IL_0014: call void [System]System.Diagnostics.Debug::WriteLine(string)
IL_0019: nop
IL_001a: ldarg.0
IL_001b: callvirt instance class [mscorlib]System.Exception [mscorlib]System.Exception::get_InnerException()
IL_0020: ldnull
IL_0021: ceq
IL_0023: stloc.0
IL_0024: ldloc.0
IL_0025: brtrue.s IL_0035
IL_0027: nop
IL_0028: ldarg.0
IL_0029: callvirt instance class [mscorlib]System.Exception [mscorlib]System.Exception::get_InnerException()
IL_002e: call void ca1.Program::DebugOutput(class [mscorlib]System.Exception)
IL_0033: nop
IL_0034: nop
IL_0035: ret
} // end of method Program::DebugOutput
因为人们错误地更关心微观优化,而不是清晰可读的代码。
递归为每个级别(您处理的每个对象)进行额外的(递归)函数调用,这也意味着堆栈分配。
通常情况下,迭代是更好的,因为它不进行额外的调用/分配。
当然,如果有多个对象要处理,则差异是显而易见的,我想您的示例中并非如此。所以对你来说,这不是一个问题,我想目的是教你更好的实践。