2010-09-02 23 views
7

Tôi có một lớp học như:Có thể viết một IEnumerable đệ quy <T>

class Spline 
    int ChildrenCount; 
    Spline GetChild (int index) 

class SplineCollection : IEnumerable<Spline> 
    Spline Master 

Có thể viết một IEnumerable đệ quy cho SplineCollection nơi mà nó sẽ trả lại tất cả những đứa trẻ từng người một?

EDIT: Vì vậy, Thầy là Hộp thư mục gốc, và hệ thống các con của nó có thể là bất kỳ chiều sâu.

EDIT: Bằng cách sử dụng tên Box, tôi nghĩ rằng tôi đã nhầm lẫn một số người. Nó có nghĩa là một vật thể hình học, không phải là một vật chứa. Vì vậy, thay đổi nó để Spline.

+1

Tôi lấy nó có nghĩa là "hậu duệ" khi bạn viết "con", vì việc nhận con không yêu cầu đệ quy. –

+0

@Job, Có bạn đúng Tôi có nghĩa là con cháu. Nó chỉ là trong sdk tôi đang sử dụng, họ vẫn được gọi là trẻ em, ChildRecursive, vì vậy đó là lý do tại sao tôi chỉ sử dụng đó. –

Trả lời

9

Thao tác này sẽ thực hiện thao tác lướt ngang đầu tiên ở độ sâu Box 'cây'. Sau đó, bạn có thể chỉ cần gọi phương thức này trên hộp Master để trả lại tất cả trẻ em đệ quy.

public class Box 
{ 
    // ... 

    public IEnumerable<Box> GetBoxes() 
    { 
     yield return this; 

     for (int i=0; i<box.ChildrenCount; i++) 
     { 
      foreach (Box child in box.GetChild(i).GetBoxes()) 
      { 
       yield return child; 
      } 
     } 
    } 
} 
+0

Cảm ơn, nhưng điều này sẽ đi qua các trẻ em của mỗi hộp.GetChild (i) quá? –

+0

Ah, bây giờ nó có ý nghĩa. Đã chỉnh sửa câu trả lời. – thecoop

1
class Box 
{ 
    int ChildrenCount; 
    Box GetChild (int index){/* some implementation*/} 
    public IEnumerable<Box> Children 
    { 
    get 
    { 
     for(int i = 0; i != ChildrenCount; ++i) 
     yield return GetChild(i); 
    } 
    } 
    public IEnumerable<Box> Descendants 
    { 
    get 
    { 
     foreach(Box child in Children) 
     { 
     yield return child; 
     foreach(Box desc in child.Descendants) 
      yield return desc; 
     } 
    } 
    } 
} 

Bạn có thể gọi đây là từ BoxCollection, nhưng kể từ Box đã là một tập hợp các hộp, tôi không thấy mục đích gì BoxCollection là ở đây. Đối với vấn đề đó, có Box thực hiện IEnumerable<Box> hoặc một trong các hậu duệ của nó (ICollection<Box>, IList<Box>) có lẽ sẽ cải thiện tính hữu ích.

Cũng có thể thực hiện nó theo cách lặp lại thay vì đệ quy, đôi khi có hiệu suất tốt hơn (bất kỳ lúc nào trình biên dịch cũng không chuyển đệ quy thành tương tác), nhưng đệ quy dễ đọc hơn và thường nhiều hơn là đủ trình diễn.

-1

Chắc chắn. Bạn thậm chí không thực sự cần một BoxContainer, vì hộp, theo tên của nó, tồn tại như một container:

public class Box 
{ 
    private List<Box> myBoxes; 

    public IEnumerable<Box> GetAllBoxes() 
    { 
     yield return this; 
     foreach (var box in myBoxes) 
     { 
      var enumerator = box.GetAllBoxes().GetEnumerator(); 
      while(enumerator.MoveNext()) 
       yield return enumerator.Current; 
     } 
    } 
} 

Nếu Box Một tổ chức Hộp B và C, hộp B tổ chức hộp D và E, và hộp C tổ chức ô F, số đếm sẽ xuất hiện A, B, D, E, C, F.

+0

Kết quả 'foreach' của bạn có hiệu quả trong kiểu trả về của 'IEnumerable >' không khớp với kiểu trả về đã khai báo. Tôi không tin rằng điều này sẽ biên dịch trong C#. Trong F # bạn có thể thay đổi 'yield' thứ hai thành' yield! 'Và nó sẽ hoạt động tốt. –

+0

Bạn hoàn toàn đúng.Chỉnh sửa nhanh để lặp qua IEnumerable được trả về bởi Box con. – KeithS

1

Có, nhưng bạn phải liệt kê kết quả đệ quy. Bạn không thể chỉ trả lại nó, bởi vì loại không khớp.

IEnumerable<int> Triangle(int n) { 
    yield return n; 
    if (n > 0) 
     foreach (var e in Triangle(n - 1)) 
      yield return e; 
} 
10

Tôi sẽ theo cách thủ công duy trì ngăn xếp thay vì dựa vào ngăn xếp cuộc gọi tại đây. Lý do là vì một IEnumerable<Spline> mới sẽ phải được tạo cho mỗi Spline được truy cập nếu bạn đã sử dụng ngăn xếp cuộc gọi bằng cách đệ quy gọi phương thức nhận con cháu. Điều đó sẽ không hiệu quả. Bạn có thể cải thiện đáng kể quá trình truyền tải bằng cách sử dụng ngăn xếp của riêng bạn.

public IEnumerable<Spline> Descendants 
{ 
    get 
    { 
     // This performs a simple iterative preorder traversal. 
     var stack = new Stack<Spline>(new Spline[] { this }); 
     while (stack.Count > 0) 
     { 
      Spline current = stack.Pop(); 
      yield return current; 
      for (int i = current.ChildrenCount - 1; i >= 0; i--) 
      { 
       stack.Push(current.GetChild(i)); 
      } 
     } 
    } 
} 
+2

Tôi ước tôi có thể bỏ phiếu này lên gấp 10 lần. Đây là * vậy * hiệu quả hơn nhiều so với giải pháp đệ quy, và chỉ cần suy nghĩ thêm vài giây nữa thôi. Trong thực tế, bạn có thể khái quát hóa điều này thành một hàm 'Flatten' để đơn giản hóa bất kỳ trình lặp nào đệ qui. –

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