2016-02-26 15 views
8

Wśród funkcjonalności znalezionych w std::algorithm nie mogę znaleźć jednego z najbardziej podstawowych, jakie mogę wymyślić: wybrałem podzbiór kolekcji (na przykład, zwróć wszystkie liczby nieparzyste, wszyscy pracownicy które mają status == "zatrudniony", wszystkie przedmioty, które kosztują mniej niż 20 dolarów).C++ "select" algorithm

więc, biorąc pod uwagę listę wskazówki jak

vector<int> ints {1, 9, 3, 27, 5, 19, 3, 8, 2, 12}; 

vector<int> evens = ? 
vector<int> greaterThan7 = ? 

jak znaleźć te, które są nawet i te, które są większe niż 7?

+12

['std :: copy_if'] (http://en.cppreference.com/w/cpp/algorithm/copy) nie działa dla ciebie? – NathanOliver

+0

Nawiasem mówiąc ... nie ma czegoś takiego jak "std :: algorithm". Prawdopodobnie chciałeś napisać ''. –

Odpowiedz

17

Na przykład

vector<int> ints {1, 9, 3, 27, 5, 19, 3, 8, 2, 12}; 
vector<int> evens; 

std::copy_if(ints.begin(), ints.end(), std::back_inserter(evens), 
       [](int x) { return x % 2 == 0; }); 

Oto poglądowe programowi

#include <iostream> 
#include <algorithm> 
#include <iterator> 
#include <vector> 

int main() 
{ 
    std::vector<int> ints { 1, 9, 3, 27, 5, 19, 3, 8, 2, 12 }; 
    std::vector<int> evens; 

    std::copy_if(ints.begin(), ints.end(), std::back_inserter(evens), 
        [](int x) { return x % 2 == 0; }); 

    for (int x : evens) std::cout << x << ' '; 
    std::cout << std::endl; 
}   

Jego wyjście jest

8 2 12 
+2

Pamiętaj, że te funkcje nie są leniwe w porównaniu do Select in C# lub innych języków, więc mogą być bardzo drogie. –

+3

Kod przykładowy pytania również nie jest leniwy.Możesz owinąć 'std :: find_if' w adapterze iteratora, jeśli potrzebujesz leniwego filtru. – Useless

+1

@MatsFredriksson wkrótce będziemy mieć zakresy do leniwej oceny. – bolov

19

Jeśli chcesz coś bardziej funkcjonalny, można sprawdzić biblioteki zakresu doładowania . Konkretnie filtered:

for (int i : ints | filtered([](int i){return i > 7;})) 
{ 
    ... 
} 

To daje leniwe widoku, bez budowy nowego pojemnika.


można uzyskać takie same od Eric Niebler na range-v3:

for (int i : view::filter(ints, [](int i){return i > 7;}) 
{ 
    ... 
} 

z korzyści, które można po prostu przypisać do wektora też (dzięki czemu można wybrać, czy jest leniwy lub chętny, który doładowania .Ranges nie pozwala).

std::vector<int> greaterThan7 = view::filter(ints, [](int i){return i > 7;}); 
std::vector<int> sameThing = ints | view::filter([](int i){return i > 7;}); 
+0

OK. Myślałem, że masz na myśli "to samo", które należy zainicjować za pomocą wersji "boost". – NathanOliver

+0

Przyspieszanie numeru NoNagOliver nie obsługuje tego. Miałem na celu wykazanie, że zakresy wspierają również rurociągi. – Barry

+0

OK. Nie wiedziałem o tym. Zawsze uczę się czegoś z twoich odpowiedzi. Dzięki. – NathanOliver

1

zależności od dokładnej wymagania, należy rozważyć std::stable_partition (lub std::partition). Zmienia elementy w zakresie tak, aby wszystkie, które spełniają predykat były pierwsze. Możesz myśleć o tym, dzieląc zakres na część "podzbiór" i "nie podzbiór". Oto przykład:

#include <algorithm> 
#include <vector> 
#include <iostream> 

int main() 
{ 
    using std::begin; 
    using std::end; 
    using std::cbegin; 
    using std::cend; 

    std::vector<int> ints { 1, 9, 3, 27, 5, 19, 3, 8, 2, 12 }; 

    auto const greater_than_7 = [](int number) { return number > 7; }; 

    auto const iter_first_not_greater_than_7 = std::stable_partition(begin(ints), end(ints), greater_than_7); 

    for (auto const_iter = cbegin(ints); const_iter != iter_first_not_greater_than_7; ++const_iter) 
    { 
     std::cout << *const_iter << "\n"; 
    } 
} 

Jeśli jednak jesteś w porządku z kopiując każdy element pasujący do nowej kolekcji, na przykład dlatego, że zakres źródłowy nie musi być modyfikowana, a następnie użyć std::copy_if.


Być może to, czego naprawdę szukasz jest widok o niemodyfikowalne zakresie. W tym przypadku zbliżasz się do problemu z niewłaściwego kierunku. Nie potrzebujesz konkretnego algorytmu; bardziej naturalnym rozwiązaniem problemu byłoby filtrowanie iteratora, jak na przykład Boost's Filter Iterator. Możesz użyć tego w Boost lub przestudiować jego implementację, aby dowiedzieć się, jak sam możesz napisać iteratory filtrujące.

+0

To właśnie miałem zamiar zasugerować. 'std :: stable_partition', konkretnie. – erip