2015-03-18 29 views
5

Tôi đã viết một hàm đệ quy trong đó sản lượng IEnumerable<int>IEnumerable đệ quy không hoạt động như mong đợi?

IEnumerable<int> Go(int b ) 
{ 
if (b==0) yield return 0; 
    else 
foreach (var element in Go(b-1)) 
    { 
    yield return element; 
    } 
} 

Vì vậy, Nếu tôi viết

foreach (var element in Go(3)) 
{ 
    Console.WriteLine (element); 
} 

Nó nên năng suất

0 
1 
2 
3 

Nhưng nó không hoạt động như mong đợi. (nó hiển thị 0).

Trong hàm đệ quy bình thường (trả về int - không có Ienumerable) nó hoạt động tốt.

Câu hỏi:

Làm thế nào tôi có thể sửa chữa đoạn code để nó mang lại giá trị mong đợi?

nb. Không có lý do gì để sử dụng đệ quy Ienumerables. Nó chỉ đến tâm trí của tôi sau khi chơi với năng suất đệ quy.

+0

nếu cần đơn giản '0 1 2 3' lý do bạn cần đệ quy? – Grundy

+0

như là một sản lượng sang một bên, đệ quy gây ra rất nhiều bộ nhớ không rõ ràng/cpu chum và như một máy trạng thái mới được xây dựng mỗi khi bạn nhập phương pháp. – spender

+0

@Grundy Vì tôi đã chơi một chút với năng suất đệ quy và không thấy kết quả mong đợi nên tôi muốn đi sâu hơn. –

Trả lời

4

Tuy nhiên, một biến thể với điều kiện b==0

static IEnumerable<int> Go(int b) 
{ 
    if (b == 0) 
    { 
     yield return 0; //return 0 if b==0; 
     yield break; // say that iteration end; 
    } 

    foreach (var el in Go(b - 1)) 
    { 
     yield return el; 
    } 

    yield return b; //return current b as element of result collection 

} 

hoặc không yield break

static IEnumerable<int> Go(int b) 
{ 
    if (b == 0) 
    { 
     yield return 0; 
    } 
    else 
    { 

     foreach (var el in Go(b - 1)) 
     { 
      yield return el; 
     } 

     yield return b; //return current b as element of result collection 
    } 
} 
+0

Mặc dù không xếp hạng cao nhất, nhưng đó là câu trả lời duy nhất tương tự với chức năng đệ quy thông thường với trực giác bình thường của điều kiện ngắt. (và tất nhiên tôi quên mất sản lượng). Điều quan trọng đối với tôi là phải sửa lại con đường bằng những góc nhỏ và không thay đổi hoàn toàn. (dĩ nhiên tôi cũng đã học được từ các câu trả lời khác, nhưng một lần nữa ....) –

6

tôi nghi ngờ rằng nó sẽ làm việc bất cứ điều gì khác nhau - bởi vì chỉ có bê tông yield tôi thấy là yield 0

Tôi đoán bạn muốn một cái gì đó như thế này:

IEnumerable<int> Go(int b) 
{ 
    if (b > 0) 
    { 
     foreach (var element in Go(b-1)) 
     { 
     yield return element; 
     } 
    } 
    yield return b; 
} 

nhưng vẫn điều này là rất hiệu quả và sẽ thổi ngăn xếp với lớn hơn b s


Đối với câu hỏi của bạn:

Mã của bạn:

sẽ làm điều này:

b=3: 
    is b == 0? no ok, then enumerate and return everything from b=2... 
    b=2: 
     is b == 0? no ok, then enumerate and return everything from b=1... 
     b=1: 
      is b == 0? no ok, then enumerate everything from b=0... 
      b=0: 
       is b == 0? **YES** so yield a single **0** 
      everything was {0} 
     everything was {0} 
    everything was {0} 
return is {0} 
+1

Tôi không hiểu vấn đề với kiểm tra 'if (b == 0)' –

+0

là gì để giải thích điều này nhiều hơn một chút – Carsten

11

Bởi vì bạn không bao giờ mang lại b bản thân nhưng chỉ mang lại 0.

IEnumerable<int> Go(int b) 
{ 
    if(b > 0) { 
     foreach (var element in Go(b-1)) 
      yield return element; 
    } 
    yield return b; 
} 

Lưu ý rằng nếu bạn muốn chuỗi kết quả bắt đầu từ 0 và đi lên trên, bạn phải yield return bsau số foreach. Hãy cuộn cuộc gọi đầu tiên cho Go(3):

Go(3): 
foreach(var element in Go(2)): 
    yield return element; 
yield return 3; 

Vì vậy, 3 sẽ là mục cuối cùng trong chuỗi (bởi vì tất cả những người khác đang yieled trước nó). Bây giờ hãy cuộn Go(2):

Go(3): 
    Go(2): 
    foreach(var element in Go(1)): 
     yield return element; 
    yield return 2; 
yield return 3; 

Go(1):

Go(3): 
    Go(2): 
     Go(1): 
      foreach(var element in Go(0)) 
       yield return element; 
     yield return 1; 
    yield return 2; 
yield return 3; 

Như bạn có thể thấy, kết quả đang bị xiềng xích "ngược" liên quan đến các cuộc gọi:

Go(3) --> Go(2) --> Go(1) --> Go(0) --> 0 --> 1 --> 2 --> 3 
+0

'thu nhập lợi nhuận b; 'và' trả về lợi nhuận 0; 'là gì? – Grundy

+0

lợi nhuận trả về b mỗi lần, lợi nhuận 0 chỉ trả về 0. – BlackBear

+2

@BlackBear Mất 2 phút để hiểu câu của bạn :-) Làm ví dụ với một hoặc hai yếu tố :-) – xanatos

0

Mã của bạn là sai lầm.:-)

Những gì nó làm là phương pháp Go chỉ bao giờ mang lại giá trị 0. Nếu được gọi với 3, sau đó nó được xuống đến cuộc gọi đệ quy cuối cùng mà trả về 0. foreach lặp lại chỉ một 0 và mang nó lần nữa. SO số không được mang lại trên mọi cấp độ như là yếu tố duy nhất.

+0

nó mang lại số không một lần –

1

Giả sử bạn có IEnumerable được gọi là enumerable. Nếu bạn viết

foreach(var element in enumerable) yield return element; 

là chính xác giống như nếu bạn viết

return enumerable; 

nếu bạn nhìn vào kết quả và kiểu trả về. Nếu bạn cố gắng

if(b == 0) yield return 0; 
else return Go(b - 1); 

Nó đưa ra một lỗi biên dịch: "Iterator không thể chứa câu lệnh return", bởi vì nếu bạn viết một hàm với một tuyên bố yield return trong nó, nó sẽ không biên dịch để một chức năng nhưng một iterator, do đó, nó không thực sự "trở lại". Hãy sửa đổi nó để có được cùng một hành vi, nhưng với một "chức năng thực sự" cho rõ ràng. Để làm cho nó biên dịch, bạn có thể sửa đổi nó để

if (b == 0) return Enumerable.Repeat(0, 1); // or return Enumerable.Range(0, 1); 
else return Go(b - 1); 

nhưng nó không thực sự làm cho nó rõ ràng hơn: Những gì bạn đã làm lên đã có gần giống như:

return b == 0 ? 0 : Go(b-1); 

nhưng kết quả được gói trong một IEnumerable. Tôi hy vọng đó là rõ ràng bây giờ tại sao nó chỉ trả về một 0.

2

Tại sao mã của bạn không hoạt động ... Chúng ta hãy bắt đầu từ cuối cùng ... Bạn muốn [0, 1, 2, 3]. Rõ ràng để có được chuỗi đó, phải có một

yield return 0 
yield return 1 
yield return 2 
yield return 3 

Nhưng trong mã của bạn, bạn có thể:

yield return 0 

hoặc

yield return the Go function 

nơi nào bạn có một số mã có thể mang trả lại phi giá trị -zero!

Lưu ý trong thực tế là mã chính xác có

yield return b 

nơi b là giá trị truyền cho hàm Go(int b), do đó chức năng đầu tiên sẽ gọi riêng của mình một cách đệ quy để trả lại valeus 0 ... b-1 và sau đó mang lại giá trị b.

1

Thêm visualization: (chỉ để hiển thị dòng chảy nếu sản lượng) Tên phương pháp theo b param

mất tôi một thời gian để xem những gì đang xảy ra ở đây.

enter image description here

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