2016-02-26 11 views
8

Tra le funzionalità trovate in std::algorithm non riesco a trovare uno dei più basilari che possa pensare: selezionato un sottoinsieme di una raccolta (ad esempio, restituire tutti i numeri dispari, tutti gli impiegati che hanno stato == 'impiegato', tutti gli articoli che costano meno di 20 dollari).Algoritmo "seleziona" C++

Quindi, data una lista di int come

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

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

Come trovare quelli che sono ancora e quelli che sono maggiori di 7?

+12

['std :: copy_if'] (http://en.cppreference.com/w/cpp/algorithm/copy) non funziona per te? – NathanOliver

+0

A proposito ... non esiste una cosa come 'std :: algorithm'. Probabilmente intendevi scrivere ''. –

risposta

17

Per esempio

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; }); 

Ecco un programma dimostrativo

#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; 
}   

La sua uscita è

8 2 12 
+2

Ricorda che queste funzioni non sono pigre, rispetto al Select in C# o ad altre lingue, quindi hanno il potenziale di essere molto costose. –

+3

Anche il codice di esempio della domanda non è pigro, per essere onesti.Potresti racchiudere 'std :: find_if' in un adattatore iteratore se hai bisogno di un filtro ponderato. – Useless

+1

@MatsFredriksson avremo presto dei range per una valutazione lazy. – bolov

19

Se volete qualcosa di più funzionale, è possibile controllare la libreria gamma spinta . In particolare, filtered:

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

questo ti dà una visione pigra, senza costruire un nuovo contenitore.


È possibile ottenere lo stesso da Eric Niebler di range-v3:

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

con il vantaggio che si può solo assegnare tale ad un vettore anche (in modo da poter decidere se è pigro o ansiosi, che Boost .Range non consente).

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. Ho pensato che intendessi 'sameThing' per essere inizializzato con la versione boost. – NathanOliver

+0

@NathanOliver No. boost non lo supporta. Volevo dimostrare che anche le gamme supportano le tubazioni. – Barry

+0

OK. Non lo sapevo. Imparo sempre qualcosa dalle tue risposte. Grazie. – NathanOliver

1

A seconda di che cosa i vostri requisiti esatti sono, in considerazione std::stable_partition (o std::partition). Riordina gli elementi nell'intervallo in modo tale che tutti quelli che soddisfano un predicato vengano prima di tutto. Si può pensare che divida l'intervallo in una parte "sottoinsieme" e una parte "non secondaria". Ecco un esempio:

#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"; 
    } 
} 

Se, tuttavia, si sta bene con la copia di ogni elemento di corrispondenza per una nuova collezione, ad esempio perché la gamma di origine non deve essere modificato, quindi utilizzare std::copy_if.


Forse quello che cercate è una vista di una gamma immodificabile. In questo caso, ti stai avvicinando al problema dalla direzione sbagliata. Non hai bisogno di un particolare algoritmo; una soluzione più naturale al problema sarebbe un filtro iteratore, come ad esempio Boost's Filter Iterator. Puoi utilizzare quello in Boost o studiarne l'implementazione per imparare come scrivere autonomamente i filtri iteratori.

+0

Questo era quello che stavo per suggerire. 'std :: stable_partition', in particolare. – erip