2014-09-26 11 views
6

Dopo C++ 11, nella libreria degli algoritmi abbiamo all_of, any_of and none_of per determinare se un predicato vale per tutti, nessuno o nessuno degli elementi di un intervallo. Una singola chiamata a uno di questi algoritmi restituisce 1 bit di informazione, mentre per un determinato intervallo e predicato ci sono 4 possibilità:Determina se un predicato vale per nessuno, alcuni o tutti gli elementi di un intervallo

  • Il predicato trattiene tutti gli elementi e senza elementi: l'intervallo è vuota;
  • Il predicato vale su tutti gli elementi (e l'intervallo non è vuoto);
  • Il predicato non contiene elementi (e l'intervallo non è vuoto);
  • Il predicato vale per alcuni ma non per tutti gli elementi.

Esiste un modo sintetico ed efficace per trovare queste informazioni? Chiamare all_of seguito da none_of è una possibilità, ma (a) non riesce a funzionare su intervalli a passaggio singolo e (b) valuta il predicato esattamente una volta più del necessario. Boost sarebbe accettabile.

+2

penso homebrew è l'unica opzione – sp2danny

+0

È possibile implementare la propria funzione con due chiamate di 'find_first_of '(una volta con il predicato e una volta con la sua negazione). O un'idea alternativa: controlla il primo manualmente poi applica "all_of" o "none_of". – Csq

+0

@Csq Ho già fatto una risposta con la prima opzione: puoi crearne una con la seconda. – Angew

risposta

10

È possibile eliminare entrambi (a) i problemi (b) se si esamina il primo elemento manualmente e si sceglie tra all_of o none_of a seconda del risultato.

Codice (preso in prestito il enum da @Angew):

enum class Result { 
    empty, all, none, some 
}; 

template <class FwIt, class Pred> 
Result examine_range(FwIt from, FwIt to, Pred pred) 
{ 
    if (from == to) return Result::empty; 
    if (pred(*from)) { 
    ++from; 
    return all_of(from,to,pred) ? Result::all : Result::some; 
    } else { 
    ++from; 
    return none_of(from,to,pred) ? Result::none : Result::some; 
    } 
} 
+0

Mi piace la semplicità di questa soluzione. Da quello che ho potuto vedere, puoi anche rilassare i requisiti in 'from' /' a' in modo da poter usare un 'InputIterator' piuttosto che un' ForwardIterator'. – Andrew

1

Perchè sono la comprensione questa domanda a torto, o si tratta di qualcosa che si potrebbe fare tramite std::accumulate?

using eana = std::tuple<bool, bool, bool, bool>; 

template <typename T, typename FwdIt, typename Pred> 
auto empty_all_none_any(FwdIt begin, FwdIt end, Pred predicate) -> eana { 
    auto result = eana{begin == end, begin != end, begin != end, false}; 
    result = std::accumulate(begin, end, result, [&](eana& res, T& val) { 
    if (predicate(val)) { 
     std::get<2>(res) = false; 
     std::get<3>(res) = true; 
    } 
    else { 
     std::get<1>(res) = false; 
    } 
    return res; 
    }); 
    return result; 
} 
+2

Il problema con la soluzione è che calcola il predicato per tutti gli elementi. Ma, per esempio, se il valore del predicato per il primo dei tuoi elementi è [vero, falso, vero, ...] potresti uscire dopo il terzo elemento dicendo che il predicato vale su alcuni ma non su tutti gli elementi. – Csq

+0

Giusto, buon punto. Questo potrebbe probabilmente essere innestato sul mio codice, ma a quel punto la tua soluzione diventa molto più elegante (se non lo è già). A proposito, c'è un modo migliore per uscire dall'accumulazione piuttosto che lanciare un'eccezione? – Kolja

0

Questo è il mio preferito algoritmo (enum da @Angew):

enum class Result { 
    empty, all, none, some 
}; 
template<class InputIt, class UnaryPredicate> 
Result examine_range(InputIt first, InputIt last, UnaryPredicate p) 
{ 
    bool all = true, none = true; 
    for (; first != last && (all || none); ++first) 
     (p(*first) ? none : all) = false; 
    return all ? (none ? Result::empty : Result::all) 
     : (none ? Result::none : Result::some); 
} 
0

È possibile farlo con le funzioni della libreria standard che descrivi. Prova entrambi se eventuali elementi sono vere, e se tutti gli elementi sono false, quindi combinare i risultati in base a questa tabella:

   any true | none true 
      ====================== 
any false | mixed | all false | 
none false | all true | empty | 
+1

Questo (a) non funziona sugli intervalli a passaggio singolo e (b) valuta il predicato esattamente una volta più del necessario. – ecatmur

Problemi correlati