2012-06-06 114 views
2

我有一個涉及STL容器的嵌套遍歷的代碼。特別是我有一個包含子列表的頂級容器(一個列表),這些子列表包含更多的子列表。對於例如在DICOM結構中,患者可以進行多項研究,每項研究可以有多個系列。我必須對Series對象執行一些操作,並且唯一的方法是深入到下面的循環中。簡化嵌套的STL容器遍歷

僞代碼看起來像這樣。

STLContainer top; 
STLContainer::iterator top_iter; 

for (top_iter= top.begin(); top_iter != top.end(); ++top_iter) { 
STLContainer mid = *top_iter; 
STLContainer::iterator mid_iter; 

for (mid_iter = mid.begin(); mid_iter!= mid.end(); ++mid_iter) { 
    STLContainer bottom = *mid_iter; 
    STLContainer::iterator bottom_iter; 

    for(bottom_iter = bottom.begin(); bottom_iter != bottom.end(); ++bottom_iter){ 
    ExecuteSomething(*bottom_iter); // Finally do something with the stored object 
    } 
} 

}

現在,如果我要重複執行這些「底部」對象上操作的系列,我不得不一次又一次地這樣做遍歷。如果我希望使用STL算法,那麼我需要爲每個嵌套級別寫入至少3行「for_each」。

有沒有人知道縮短這個代碼的技巧,可以這樣工作?

// Drills down to the deepest nested container 
for_each(top.begin(), top.end(), DrillDownAndExecuteOnBottom()); 

哪個可以在一條線上工作?像這樣? 謝謝!

+1

曾經有一個_Boost proposed_ iterator,展現了嵌套容器。 –

+1

你可以製作一個對單個對象和容器(甚至是單個對象,單個元素容器和映射類型容器)都適用的通用'iterate'模板,它對單個元素執行實際操作,但是對容器進行迭代。 –

回答

2

假設容器不都是相同的元素類型的:

struct DrillDownAndExecuteOnBottom 
{ 
    template<typename T> 
    void operator()(T& t) const 
    { for_each(t.begin(), t.end(), *this); } 

    void operator()(Bottom& b) const 
    { ExecuteSomething(b); } 
}; 

,直到它到達Bottom對象這會是一個深度優先遍歷。

+0

我更喜歡這個,感謝Jonathan,JAB和Brangdon在您的時間和幫助! –

0

下面是一些半僞碼遞歸函數應該做的伎倆:

void iterate(Container c) 
{ 
    for (Iterator iter = c.begin(); iter != c.end(); iter++) 
    { 
     if (*iter is a Container) // I forget how to do type-checking in C++ 
      iterate(*iter); 
     else 
      ExecuteSomething(*iter); 
    } 
} 

編輯:

如果C++處理函數指針不同

像這樣的事情可能會更靈活(不記得從C /有利用它們的更好的方式,但不管)

void recursiveIterateAndExecute(Container c, void func(Container)) 
{ 
    for (Iterator iter = c.begin(); iter != c.end(); iter++) 
    { 
     if (*iter is a Container) // I forget how to do type-checking in C++ 
      recursiveIterateAndExecute(*iter, func); 
     else 
      func(*iter); 
    } 
} 

有可能修改這些更符合STL算法線的方式,但我沒有用C++做過很多工作,我無法真正對此發表評論。

+0

嗯井遞歸只是另一種方式來做同樣的事情。我想知道是否有一些使用標準庫的魔法代碼,甚至Boost。但是現在感覺像編寫一個自定義迭代器是@kerreck建議的方式。 –

+0

@VikasB kerreck建議的幾乎是我給你的,除了我的靈活性稍差一些,因爲它只是簡單地使用頂級容器,「在單個元素上執行實際操作,但在容器上迭代」,所以我想象自定義迭代器的實際實現最終會非常相似(如果我沒有弄錯,您必須跟蹤迭代的每個當前項的更高級迭代器,並且使用它更容易遞歸設置)。 – JAB

+0

(當然,遞歸併不是必須的,但是你最終可能最終會使用一個堆棧[像]實體來跟蹤更高級的迭代器,所以你可能只是讓編譯器爲你做,除非你真的感覺需要爲此優化代碼。) – JAB

1

您可以編寫遍歷一次並使用lambdas封裝它。

void for_each_bottom(Top &top, const std::function<void(Bottom &)> &fn) { 
    for (auto t = top.begin(); t != top.end(); ++t) 
     for (auto m = t->begin(); m != t->end(); ++m) 
      for (auto b = m->begin(); b != b->end(); ++b) 
       fn(*b); 

} 

void demo(Top &top) { 
    for_each_bottom(top, [](Bottom &bottom) { 
     ExecuteSomething(bottom); 
    }); 
} 

(如果你喜歡的遍歷使用喬納森Wakely的遞歸/模板的方法;這些嵌套的循環是簡單,但不太通用和使用模板類型來代替std ::功能<>如果你喜歡的,太。我通常更喜歡避免使用模板,除非需要它們。)

+0

這和其他一些建議幾乎相當於訪問者模式,這是一次很好的寫入迭代代碼的方法。 http://en.wikipedia.org/wiki/Visitor_pattern –