Wracając do C ++ po latach C #, zastanawiałem się jaki byłby nowoczesny - czytaj: C ++ 11 - sposób filtrowania tablicy, czyli jak możemy osiągnąć coś podobnego do tego zapytania Linq:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Aby przefiltrować wektor elementów ( strings
ze względu na to pytanie)?
Mam szczerą nadzieję, że stare algorytmy w stylu STL (lub nawet rozszerzenia, takie jak boost::filter_iterator
) wymagające zdefiniowania jawnych metod, zostały już zastąpione?
filterProperty
ustawione natrue
?Odpowiedzi:
Zobacz przykład z cplusplus.com dla
std::copy_if
:std::vector<int> foo = {25,15,5,-5,-15}; std::vector<int> bar; // copy only positive numbers: std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );
std::copy_if
oblicza wyrażenie lambda dla każdego elementu w tymfoo
miejscu i jeśli zwracatrue
, kopiuje wartość dobar
.std::back_inserter
Pozwala nam rzeczywiście wstawić nowe elementy na końcubar
(za pomocąpush_back()
) z iteracyjnej bez konieczności zmiany rozmiaru go do wymaganej wielkości pierwszy.źródło
std::copy_if
to nic innego jak pętla forBardziej wydajnym podejściem, jeśli faktycznie nie potrzebujesz nowej kopii listy, jest
remove_if
usunięcie elementów z oryginalnego kontenera.źródło
remove_if
szczególnie mi się podoba, ponieważ jest to sposób na użycie filtra w obecności mutacji, co jest szybsze niż kopiowanie całej nowej listy. Gdybym robił filtrowanie w C ++, użyłbym tego od nowacopy_if
, więc myślę, że dodaje.remove_if
nie zmieniasize()
. Musisz łańcucha jąerase
za to .W C ++ 20 użyj widoku filtru z biblioteki zakresów: (wymaga
#include <ranges>
)// namespace views = std::ranges::views; vec | views::filter([](int a){ return a % 2 == 0; })
leniwie zwraca parzyste elementy
vec
.(Zobacz [range.adaptor.object] / 4 i [range.filter] )
Jest to już obsługiwane przez GCC 10 ( demo na żywo ). W przypadku Clang i starszych wersji GCC można również użyć oryginalnej biblioteki range-v3 z
#include <range/v3/view/filter.hpp>
(lub#include <range/v3/all.hpp>
) iranges::views
przestrzenią nazw zamiaststd::ranges::views
( live demo ).źródło
Myślę, że Boost.Range też zasługuje na wzmiankę. Wynikowy kod jest bardzo zbliżony do oryginału:
#include <boost/range/adaptors.hpp> // ... using boost::adaptors::filtered; auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; });
Jedynym minusem jest jawne zadeklarowanie typu parametru lambdy. Użyłem decltype (elements) :: value_type, ponieważ pozwala to uniknąć konieczności przeliterowania dokładnego typu, a także dodaje ziarno ogólności. Alternatywnie, w przypadku polimorficznych lambd C ++ 14 typ można po prostu określić jako auto:
auto filteredElements = elements | filtered([](auto const& elm) { return elm.filterProperty == true; });
FilterElements byłby zakresem odpowiednim do przechodzenia, ale jest to w zasadzie widok oryginalnego kontenera. Jeśli potrzebujesz kolejnego kontenera wypełnionego kopiami elementów spełniających kryteria (tak, aby był niezależny od czasu życia oryginalnego kontenera), może to wyglądać następująco:
using std::back_inserter; using boost::copy; using boost::adaptors::filtered; decltype(elements) filteredElements; copy(elements | filtered([](decltype(elements)::value_type const& elm) { return elm.filterProperty == true; }), back_inserter(filteredElements));
źródło
Moja sugestia dotycząca odpowiednika C ++ w C #
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Zdefiniuj funkcję szablonu, do której przekazujesz predykat lambda w celu wykonania filtrowania. Funkcja szablonu zwraca przefiltrowany wynik. na przykład:
template<typename T> vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate) { vector<T> result; copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate); return result; }
używać - podając trywialne przykłady:
std::vector<int> mVec = {1,4,7,8,9,0}; // filter out values > 5 auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); }); // or > target int target = 5; auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });
źródło
Ulepszony kod pjm zgodny z sugestiami podkreślenia-d :
template <typename Cont, typename Pred> Cont filter(const Cont &container, Pred predicate) { Cont result; std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate); return result; }
Stosowanie:
std::vector<int> myVec = {1,4,7,8,9,0}; auto filteredVec = filter(myVec, [](int a) { return a > 5; });
źródło