2013-08-22 87 views
16

Mùi thiết kế, thực hành kém trong đệ quy? sau khi tôi thấy việc chia sẻ lại đề xuất cải tiến, tôi nhanh chóng xem xét trên google. Đã thấy nhiều nhận xét xung quanh việc đệ quy lại đuôi cho các lần lặp lại và đề cập đến nó như một mùi thiết kế.Tại sao lặp lại nên được sử dụng thay vì đệ quy đuôi?

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; 
    } 
} 

CHỈNH SỬA: Được gửi vào bình luận điều trị trình biên dịch C#. Có vẻ như nó đang đệ quy bây giờ
Mục tiêu .net 4.5. C# 5.0

ILDASM Output cho phiên bản đệ quy đuôi: Hiển thị cuộc gọi đệ quy và không lặp

.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 
+1

Lưu ý rằng, trong khi CLR hỗ trợ các cuộc gọi đuôi, [trình biên dịch C# không phát ra chúng] (http://www.thomaslevesque.com/2011/09/02/tail-recursion-in-c/) (thời gian qua tôi đã kiểm tra). Trong mọi trường hợp, nó không chắc rằng bạn sẽ thổi các ngăn xếp với cấu trúc đặc biệt này, và hiệu suất không thực sự là một vấn đề vì bạn đã đối phó với ngoại lệ anyway. –

+4

Tôi sẽ từ chối rằng 'while' loop ủng hộ một cái gì đó gần gũi hơn với' while (ex! = Null) {Debug.WriteLine (ex.Message); ex = ex.InnerException} '. – Kevin

+0

Hãy xem xét những gì xảy ra khi sử dụng đệ quy đuôi từ góc độ hiệu suất. Chúng ta cần tạo một khung ngăn xếp, sao chép một số thứ vào và gọi rồi gọi hàm. Tùy thuộc vào đệ quy, điều này có thể dẫn đến một chồng bị thổi (mặc dù không có trong ví dụ này) và hiệu suất tồi tệ hơn mà không lặp lại đệ quy – rileyberton

Trả lời

16

Bởi vì mọi người nhầm lẫn quan tâm nhiều hơn về vi tối ưu hơn so với mã rõ ràng và dễ đọc.

+2

tôi thích giải pháp đệ quy DRY dễ đọc và duy trì hơn tiết kiệm vài nano giây. Tâm trí bạn một sự lặp lại rất thanh lịch đã được nhấn mạnh bởi kevin. Như thường lệ, bạn có thể viết những cuộc thám hiểm tốt và xấu. Nhưng gọi đệ quy một mùi hôi dường như trên đầu tôi. Đặc biệt khi kết quả thiết kế SOLID áp dụng. Vì vậy, +1 ngay bây giờ. sẽ chờ xem điều gì xảy ra trước khi chấp nhận làm câu trả lời. Cảm ơn ILMTitan –

+0

Bạn cũng có thể loại bỏ kiểm tra rỗng thừa trên InnerException trong giải pháp đệ quy. – ILMTitan

8

recursion thực hiện cuộc gọi hàm bổ sung (đệ quy), điều này cũng có nghĩa là phân bổ ngăn xếp cho mọi cấp (mọi đối tượng bạn xử lý).

Thông thường việc lặp lại là better bởi vì nó không thực hiện cuộc gọi/phân bổ bổ sung đó.

Tất nhiên, sự khác biệt sẽ được hiển thị trong trường hợp có nhiều đối tượng để xử lý, mà tôi cho rằng không phải như vậy trong ví dụ của bạn. Vì vậy, đối với bạn nó không phải là một vấn đề và tôi đoán ý định là để dạy cho bạn một thực hành tốt hơn nói chung.

Các vấn đề liên quan