2012-02-28 11 views
8

Powiedzmy, że mam wektor elementów i macierz mas, i chcę wyodrębnić elementy z wektora z prawdziwą odpowiednią wartością maski, aby oddzielić wektor. Czy istnieje sposób użycia std::copy_if do tego celu? Problem polega na tym, że mam tylko wartość elementu wewnątrz predykatu, a nie iterator do niego, więc nie mogę znać rzeczywistego indeksu do adresu tablicy mas.Jak uzyskać indeks elementu z predykatu przekazanego do jakiegoś algorytmu STL?

mogę bezpośrednio manipulować adresów tak:

vector<bool> mask; 
vector<int> a, b; 
copy_if(a.begin(), a.end(), b.begin(), [&] (int x) -> bool { 
    size_t index = &x - &a[0]; // Ugly... 
    return mask[index]; 
}); 

Jednak uważam, że jest to brzydkie rozwiązanie. Jakieś lepsze pomysły?

Aktualizacja: Innym możliwym rozwiązaniem jest zastosowanie zewnętrznego iterację na maskę:

vector<bool> mask; 
vector<int> a, b; 
auto pMask = mask.begin(); 
copy_if(a.begin(), a.end(), b.begin(), [&] (int x) { 
    return *pMask++; 
}); 

Jednakże rozwiązanie to wymaga dodatkowej zmiennej w zewnętrznej przestrzeni nazw, które jeszcze nie jest pożądane.

Odpowiedz

4

Ok, po nieco dochodzenia wyjdę z pierwszego przykładu być najprostszym droga. Jednak nie należy zapominać o przekazywaniu wartości w odwołaniu lambda by (const), aby nie przyjmować adresu lokalnej kopii parametru:

copy_if(a.begin(), a.end(), b.begin(), 
    [&] (const int& x) -> bool { // <-- do not forget reference here 
    size_t index = &x - &a[0]; // Still ugly... but simple 
    return mask[index]; 
    }); 
1

Można połączyć kilka iteratory tworzą wzmocnienia (nie do końca przetestowane, ale kompiluje z GCC 4.6):

#include <algorithm> 

#include <boost/iterator/counting_iterator.hpp> 
#include <boost/iterator/zip_iterator.hpp> 
#include <boost/iterator/filter_iterator.hpp> 
#include <boost/tuple/tuple.hpp> 

int main() { 
    std::vector<bool> mask; 
    std::vector<int> a, b; 
    boost::counting_iterator<size_t> count_begin(0), count_end(a.size()); 
    auto zip_begin = boost::make_zip_iterator(boost::make_tuple(count_begin, a.begin())); 
    auto zip_end = boost::make_zip_iterator(boost::make_tuple(count_end, a.end())); 
    typedef decltype(zip_end) zip_iterator; 
    typedef const zip_iterator::value_type& zip_value; 
    auto pred = [&mask](zip_value val) { 
    auto index = val.get<0>(); 
    return index < mask.size() ? mask[index] : true; 
    }; 
    auto filter_begin = boost::make_filter_iterator(pred, zip_begin, zip_end); 
    auto filter_end = boost::make_filter_iterator(pred, zip_end, zip_end); 
    std::transform(filter_begin, filter_end, back_inserter(b), [](zip_value val) { 
     return val.get<1>(); 
    }); 
} 

Myślę jednak, że wyraźna pętla jest tylko prostszy tutaj.


Oto kolejny bardziej uogólniona wersja powyższego kodu, tym razem nawet przetestowane :) Zapewnia implementacje dla Python podobnych map, filter i enumerate funkcji. Ten wymaga GCC 4.7.

#include <utility> 
#include <vector> 
#include <iterator> 
#include <type_traits> 
#include <iostream> 

#define BOOST_RESULT_OF_USE_DECLTYPE 

#include <boost/tuple/tuple.hpp> 
#include <boost/iterator/zip_iterator.hpp> 
#include <boost/iterator/filter_iterator.hpp> 
#include <boost/iterator/transform_iterator.hpp> 
#include <boost/range/begin.hpp> 
#include <boost/range/end.hpp> 
#include <boost/range/size.hpp> 
#include <boost/range/iterator_range.hpp> 
#include <boost/range/counting_range.hpp> 
#include <boost/range/algorithm/copy.hpp> 
#include <boost/range/algorithm_ext/push_back.hpp> 

template<typename... ForwardRange> 
using zip_range = boost::iterator_range< 
    boost::zip_iterator< 
    boost::tuple< 
     typename boost::range_iterator< 
     typename std::remove_reference<ForwardRange>::type>::type...>>>; 

template<typename... ForwardRange> 
zip_range<ForwardRange...> 
zip(ForwardRange&&... ranges) { 
    return boost::make_iterator_range(
    boost::make_zip_iterator(
     boost::make_tuple(
     boost::begin(std::forward<ForwardRange>(ranges))...)), 
    boost::make_zip_iterator(
     boost::make_tuple(
     boost::end(std::forward<ForwardRange>(ranges))...))); 
} 

template<typename ForwardRange, typename Index> 
using enumerating_range = zip_range< 
    boost::iterator_range<boost::counting_iterator<Index>>, 
    ForwardRange>; 

template<typename ForwardRange, typename Index> 
enumerating_range<ForwardRange, Index> 
enumerate(ForwardRange&& range, Index start) { 
    return zip(
    boost::counting_range(
     start, 
     static_cast<Index>(start + boost::size(range))), 
    std::forward<ForwardRange>(range)); 
} 

template<typename Predicate, typename ForwardRange> 
using filter_range = boost::iterator_range< 
    boost::filter_iterator< 
    Predicate, 
    typename boost::range_iterator< 
     typename std::remove_reference<ForwardRange>::type>::type>>; 

template<typename Predicate, typename ForwardRange> 
filter_range<Predicate, ForwardRange> 
filter(Predicate pred, ForwardRange&& range) { 
    return boost::make_iterator_range(
    boost::make_filter_iterator(
     pred, 
     boost::begin(std::forward<ForwardRange>(range))), 
    boost::make_filter_iterator(
     pred, 
     boost::end(std::forward<ForwardRange>(range)))); 
} 

template<typename UnaryOperation, typename ForwardRange> 
using map_range = boost::iterator_range< 
    boost::transform_iterator< 
    UnaryOperation, 
    typename boost::range_iterator< 
     typename std::remove_reference<ForwardRange>::type>::type>>; 

template<typename UnaryOperation, typename ForwardRange> 
map_range<UnaryOperation, ForwardRange> 
map(UnaryOperation operation, ForwardRange&& range) { 
    return boost::make_iterator_range(
    boost::make_transform_iterator(
     boost::begin(std::forward<ForwardRange>(range)), 
     operation), 
    boost::make_transform_iterator(
     boost::end(std::forward<ForwardRange>(range)), 
     operation)); 
} 

template<typename UnaryOperation, typename Predicate, typename ForwardRange> 
using filter_map_range = map_range< 
    UnaryOperation, 
    filter_range<Predicate, ForwardRange>>; 

template<typename UnaryOperation, typename Predicate, typename ForwardRange> 
filter_map_range<UnaryOperation, Predicate, ForwardRange> 
filter_map(UnaryOperation operation, Predicate pred, ForwardRange&& range) { 
    return map(operation, filter(pred, range)); 
} 

int main() { 
    std::vector<int> a { 10, 11, 12, 13, 14 }; 
    std::vector<bool> mask { false, true, true, false, true }; 
    std::vector<int> b; 
    auto enumerator = enumerate(a, 0u); 
    typedef boost::range_value<decltype(enumerator)>::type enum_value; 
    boost::push_back(
    b, 
    filter_map(
     [](const enum_value& val) { 
     return val.get<1>(); 
     }, 
     [&mask](const enum_value& val) { 
     auto i = val.get<0>(); 
     return i < mask.size() ? mask[i] : true; 
     }, 
     enumerator)); 
    boost::copy(b, std::ostream_iterator<int>(std::cout, " ")); 
    std::cout << std::endl; 
} 

Jeśli nie są zobowiązane do stosowania wektorów, roztwór staje się nieco nudne:

#include <valarray> 
#include <algorithm> 
#include <iterator> 
#include <iostream> 

int main() { 
    using namespace std; 
    valarray<int> a { 10, 11, 12, 13, 14 }; 
    valarray<bool> mask { false, true, true, false, true }; 
    valarray<int> b = a[mask]; 
    copy(begin(b), end(b), ostream_iterator<int>(cout, " ")); 
} 
Powiązane problemy