2012-04-02 64 views
11

lambda表示法使得stl算法更易於訪問。我仍然在學習如何決定什麼時候有用,什麼時候回到老式的循環。 通常,就有必要迭代相同大小的兩個(或多個)容器中,以使得相應的元件是相關的,但由於某些原因未打包到同一類別。std :: for_each處理多個迭代器範圍

一個函數中使用一個for循環來實現,將是這樣的:

template<typename Data, typename Property> 
void foo(vector<Data>& data, vector<Property>& prop) { 
    auto i_data = begin(data); 
    auto i_prop = begin(prop); 
    for (; i_data != data.end(); ++i_data, ++i_prop) { 
     if (i_prop->SomePropertySatistfied()) { 
      i_data->DoSomething(); 
     } 
    } 
} 

爲了使用for_each的,我需要一個版本,它是處理多個範圍;是這樣的:

template<typename InputIter1, typename InputIter2, typename Function> 
Function for_each_on_two_ranges(InputIter1 first1, InputIter1 last1, InputIter2 first2, Function f) { 
    for (; first1 != last1; ++first1, ++first2) { 
     f(*first1, *first2); 
    } 
    return f; 
} 

在這個版本中,上面的代碼應該是這樣的:

template<typename Data, typename Property> 
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) { 
    for_each_on_two_ranges(begin(data), end(data), begin(prop), [](Data& d, Property& p) { 
     if (p.SomePropertySatistfied()) { 
      d.DoSomething(); 
     } 
    }); 
} 

有沒有達到使用STL算法同樣結果的等效方法是什麼?

編輯

我找到確切的答案,我的問題中的boost ::升壓::範圍運行的for_each的形式。爲了完整起見,我添加了答案和示例代碼。

+1

爲什麼不直接使用已經寫好的'for_each_two_ranges'? – Puppy 2012-04-02 11:58:28

+1

它看起來像我這麼普遍,我認爲它已經被某人已經解決了 – killogre 2012-04-02 12:01:25

+6

我認爲來自Boost.Iterator的'zip_iterator'做你想做的。有關詳細信息,請參閱http://www.boost.org/doc/libs/1_49_0/libs/iterator/doc/zip_iterator.html。 – celtschk 2012-04-02 12:23:51

回答

8

1)在STL的算法並不意味着涵蓋所有可能的情況下,如果你需要for_each_on_two_ranges然後寫它(如你),並使用它。 STL的美妙之處在於它的可擴展性,並且已經用一個有用的新算法擴展了它。

2)如果不工作,你不必使用傳統風格的for循環,您可以使用花哨的新for循環,而不是!

作爲另一個答案說,boost::zip_iterator是你的朋友在這裏,但它並沒有將很難使用。下面是使用與zip_iterator

template<typename Data, typename Property> 
void foo(vector<Data>& data, vector<Property>& prop) { 
    for (auto i : redi::zip(data, prop)) 
     if (i.get<1>().SomePropertySatistfied()) 
      i.get<0>.DoSomething(); 
} 

zip函數創建與begin()end()成員返回boost::zip_iterator的適配器實現的範圍適配器的溶液中,所以循環變量是每個基礎容器中的元素的元組(並且因爲它是一個可變參數模板可以爲任意數量的容器做到這一點,所以你不需要寫for_each_for_three_rangesfor_each_for_four_ranges等)

您也可以與for_each

使用10
auto z = redi::zip(data, prop); 
typedef decltype(z)::iterator::reference reference; 

for_each(begin(z), end(z), [](reference i) { 
    if (i.get<1>().SomePropertySatistfied()) { 
     i.get<0>().DoSomething(); 
    } 
}); 
+0

這看起來很不錯。我喜歡它可變的事實,並且代碼相對緊湊。我發現我需要在範圍適配器上做些功課。謝謝! – killogre 2012-05-05 17:30:55

+0

redi是一個命名空間的提升?什麼是必需的標題? Thankee。 – 2012-08-22 21:27:11

+0

@JiveDadson,不,這是我自己的命名空間,我將工作[zip](https://gitorious.org/redistd/redistd/blobs/master/include/redi/zip.h)鏈接到源代碼(並再次執行現在) – 2012-08-22 21:34:16

1

std::transform具有並行地對兩個序列進行操作的過載。如果你不想收集任何結果,你需要一個空輸出迭代器來吸收結果。

+0

for_each和transform有不同的語義[鏈接](http://drdobbs.com/cpp/184403769)。該標準要求變換是非變異的,但不要求來自for_each。無論如何,你提到的重載只適用於相同的迭代器 – killogre 2012-04-02 17:28:53

+0

@Killogre:'std :: transform'允許通過將輸出迭代器設置爲其中一個輸入迭代器來允許變化。但是,這不是真的爲這種確切的情況設計的。 – 2012-04-02 18:56:28

5

上的boost :: zip_iterator和boost :: iterator_range的一些答案的建議閱讀後,我遇到了extension algorithms in boost::range,發現我寫了兩個範圍的算法的精確平行,但與升壓範圍。

的例子

的工作準則是

#include <boost/range/algorithm_ext/for_each.hpp> 

template<typename Data, typename Property> 
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) { 
    auto rng1 = boost::make_iterator_range(data.begin(), data.end()); 
    auto rng2 = boost::make_iterator_range(prop.begin(), prop.end()); 
    boost::for_each(rng1, rng2, [](Data& d, Property& p) { 
     if (p.SomePropertySatistfied()) { 
      d.DoSomething(); 
     } 
    }); 
} 

一些包裝和效用函數,考慮到什麼@Jonathan Wakely建議類似,可以讓這個更可用。

+0

包含路徑應​​該總是使用/不\ \即使在Windoze上,否則路徑\名稱\像\ this.hpp「'將有新行和製表符字符。再加上它使它們可以移植到所有系統,而不僅僅是Windoze。 – 2012-08-22 22:44:26

+0

這段代碼現在是Unix友好的,@JonathanWakely :) – killogre 2012-08-30 06:20:22

+2

請注意,您可以只寫'boost :: for_each(data,prop,...)' - 不需要調用'make_iterator_range'。 – ecatmur 2014-02-13 09:28:09