2010-11-23 86 views
5

考慮以下幾點:LINQ和遞歸

public class Box 
{ 
    public BoxSize Size { get; set; } 

    public IEnumerable<Box> Contents { get; set; } 
} 

Box FindBoxBySize(Box box, BoxSize size) 
{ 
    Box _foundBox = null; 

    Action<IEnumerable<Box>> _recurse = null; 

    _recurse = new Action<IEnumerable<Box>>(boxes => 
    { 
     foreach (var _box in boxes) 
     { 
      if (_box.Size == size) 
      { 
       _foundBox = _box; 

       return; 
      } 

      if (_box.Contents != null) _recurse(_box.Contents); 
     } 
    }); 

    _recurse(box.Contents); 

    return _foundBox; 
} 

有沒有什麼辦法可以FindBoxBySize()使用LINQ被壓縮?另外:歡迎對我的代碼發表評論。我沒有做太多的遞歸,所以我可能在我的實現中遺漏了一些東西。

回答

5

我還需要擴展方法的方法,但使用iterator方法:

public static class BoxEx 
{ 
    public static IEnumerable<Box> Flatten(this Box box) 
    { 
     yield return box; 
     if (box.Contents != null) 
     { 
      foreach (var b in box.Contents.SelectMany(b2 => Flatten(b2))) 
      { 
       yield return b; 
      } 
     } 
    } 
} 

FindBoxBySize方法現在變成了:

Box FindBoxBySize(Box box, BoxSize size) 
{ 
    return (from b in box.Flatten() 
      where b.Size == size 
      select b).FirstOrDefault(); 
} 

你的原始調用代碼工作無需修改:

var small = FindBoxBySize(box, BoxSize.Small); 
2

擴展方法:

class Box 
{ 
    public IEnumerable<Box> GetBoxes() 
    { 
     // avoid NullReferenceException 
     var contents = this.Contents ?? Enumerable.Empty<Box>(); 

     // do the recursion 
     return contents.SelectMany(b => b.GetBoxes()).Concat(contents); 
    } 

    public Box GetBox(int size) 
    { 
     return this.GetBoxes().FirstOrDefault(b => b.Size == size); 
    } 
} 

用法:

var box_with_box = new Box { Contents = new[] { new Box() { Size = 10 } } }; 
var box = new Box { Contents = new[] { box_with_box, box_with_box } }; 

Box box10 = box.GetBox(10); 
+0

我認爲它的確如此。但請注意我的訣竅;我真的很喜歡那裏儘可能少的「循環」,所以無論何時找到一場比賽,我想終止。 – roosteronacid 2010-11-23 14:49:00

1

如果你想使用LINQ,你可以做這樣的事情(未經測試):

public static IEnumerable<Box> GetBoxesRecursive(this Box box) 
{ 
    if(box == null) 
     throw new ArgumentNullException("box"); 

    var children = box.Contents ?? Enumerable.Empty<Box>(); 

          // use lambda in C# 3.0  
    var recursiveChildren = children.SelectMany(GetBoxesRecursive); 

    return new[] { box }.Concat(recursiveChildren);          
} 

...  

Box desiredBox = myBox.GetBoxesRecursive() 
         .FirstOrDefault(b => b.Size == desiredSize); 
0

你可以做你的遞歸更清晰一些(在我眼裏)通過編寫

Box FindBoxBySize(Box box, BoxSize size) 
{ 
    if (box.Size == size) 
     return box; 

    foreach (var child in box.Contents) 
    { 
     var foundBox = FindBoxBySize(child, size); 
     if(foundBox != null) 
      return foundBox; 
    } 

    return null; 
} 

至於LINQ:LINQ沒有處理遞歸數據結構的好方法。可以通過向谷歌詢問「LINQ遞歸」來找到幾種不同的擴展方法來彌補,但我不確定這些方法是否增加了這種情況下的清晰度。