2015-05-24 8 views
6

L'utilizzo di range-v3 library (di @EricNiebler) rende la scrittura di codice algoritmico molto più compatta, ad es. ecco come generare un po 'di numeri casuali:Come scrivere un'azione range-v3 per random_shuffle?

#include <range/v3/all.hpp> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    using namespace ranges; 

    auto const N = 10; 
    std::vector<int> v; 
    v.reserve(N); 

    v |= action::push_back(view::iota(0, N)); 
    random_shuffle(v); 
    copy(v, ostream_iterator<>(std::cout, ",")); 
} 

Live Example.

Tuttavia, io preferirei di estendere la pipeline con un ipotetico action::random_shuffle() come questo

v |= action::push_back(view::iota(0, N)) | action::random_shuffle(); 

Ecco il mio tentativo di scrivere tale un'azione (sfortunatamente, scrivere un nuovo codice range-v3 è un po 'più prolisso che usare la libreria)

#include <functional> // bind, placeholders::_1 

namespace ranges 
{ 
    inline namespace v3 
    { 
     /// \addtogroup group-actions 
     /// @{ 
     namespace action 
     { 
      struct random_shuffle_fn 
      { 
      private: 
       friend action_access; 

       static auto bind(random_shuffle_fn random_shuffle) 
       RANGES_DECLTYPE_AUTO_RETURN 
       (
        std::bind(random_shuffle, std::placeholders::_1) 
       ) 

       template<typename Gen> 
       static auto bind(random_shuffle_fn random_shuffle, Gen && rand) 
       RANGES_DECLTYPE_AUTO_RETURN 
       (
        std::bind(random_shuffle, std::placeholders::_1, bind_forward<Gen>(rand)) 
       ) 
      public: 
       struct ConceptImpl 
       { 
        template<typename Rng, 
         typename I = range_iterator_t<Rng>> 
        auto requires_(Rng&&) -> decltype(
         concepts::valid_expr(
          concepts::model_of<concepts::RandomAccessRange, Rng>(), 
          concepts::is_true(Permutable<I>()) 
         )); 
       }; 

       template<typename Rng> 
       using Concept = concepts::models<ConceptImpl, Rng>; 

       template<typename Rng, 
        CONCEPT_REQUIRES_(Concept<Rng>())> 
       Rng operator()(Rng && rng) const 
       { 
        ranges::random_shuffle(rng); 
        return std::forward<Rng>(rng); 
       } 

       template<typename Rng, typename Gen, 
        CONCEPT_REQUIRES_(Concept<Rng>())> 
       Rng operator()(Rng && rng, Gen && rand) const 
       { 
        ranges::random_shuffle(rng, std::forward<Gen>(rand)); 
        return std::forward<Rng>(rng); 
       } 

       #ifndef RANGES_DOXYGEN_INVOKED 
       template<typename Rng> 
       void operator()(Rng &&) const 
       { 
        CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(), 
         "The object on which action::random_shuffle operates must be a model of the " 
         "RandomAccessRange concept."); 
        using I = range_iterator_t<Rng>; 
        CONCEPT_ASSERT_MSG(Permutable<I>(), 
         "The iterator type of the range passed to action::random_shuffle must allow its " 
         "elements to be permuted; that is, the values must be movable and the " 
         "iterator must be mutable."); 
       } 
      #endif 
      }; 

      /// \ingroup group-actions 
      /// \relates sort_fn 
      /// \sa `action` 
      namespace 
      { 
       constexpr auto&& random_shuffle = static_const<action<random_shuffle_fn>>::value; 
      } 
     } 
     /// @} 
    } 
} 

Live Example che non riesce a compilare perché alcuni operator() profondamente nascosto da qualche parte non viene trovato.

Per quanto posso vedere, ho trasposto fedelmente il codice precedente da codice simile per es. il action::sort(). L'unica differenza è che lo random_shuffle() ha due sovraccarichi (uno prende un generatore casuale), mentre tutte le altre azioni (incluso sort) hanno un unico sovraccarico con valori predefiniti per i loro parametri aggiuntivi (comparatori, predicati, proiettori ecc.). Ciò si traduce in due funzioni di membro statico bind() di random_shuffle_fn precedenti, mentre tutte le altre azioni hanno un solo sovraccarico bind().

Domanda: come scrivere un'azione range-v3 per random_shuffle?

+0

Non completamente pertinente alla domanda, ma: 'std :: random_shuffle' è deprecato. Usa 'std :: shuffle'. – Xeo

+0

In termini di stile, penso che preferirei 'std :: vector v = view :: iota (0, N); v | = action :: random_shuffle; 'o anche' auto v = std :: vector {view :: iota (0, N)} | action :: random_shuffle; 'per eliminare la chiamata' reserve'. – Casey

+0

@Casey chiamerà 'iota'' reserve'? – TemplateRex

risposta

3

Hai due overload ambigue di random_shuffle_function::operator()(Rng&&), il vostro "errore di cattura" sovraccarico deve essere vincolata ad accettare solo quelle argomentazioni che gli scarti di sovraccarico corrette (abbiamo davvero bisogno Concetti C++ così non ho mai più avere a SFINAE vincolare sovraccarichi) :

#ifndef RANGES_DOXYGEN_INVOKED 
template<typename Rng, 
    CONCEPT_REQUIRES_(!Concept<Rng>())> 
void operator()(Rng &&) const 
{ 
    CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(), 
     "The object on which action::random_shuffle operates must be a model of the " 
     "RandomAccessRange concept."); 
    using I = range_iterator_t<Rng>; 
    CONCEPT_ASSERT_MSG(Permutable<I>(), 
     "The iterator type of the range passed to action::random_shuffle must allow its " 
     "elements to be permuted; that is, the values must be movable and the " 
     "iterator must be mutable."); 
} 
#endif 

Inoltre, è necessario tubo attraverso action::random_shuffle:

v |= action::push_back(view::iota(0, N)) | action::random_shuffle; 

DEMO

+0

ottimo, grazie per averlo fatto funzionare. L'assenza di CONCEPTS_REQUIRE è stata una pura svista, dal taglio di alcuni parametri aggiuntivi del template che ho copiato dal codice 'action :: sort'. Non so perché ho messo la parentesi su action :: random_shuffle', forse perché mi aspettavo che fosse un oggetto funzione nullo. Quindi cosa viene chiamato qui? – TemplateRex

+1

un altro commento: per favore considera di fare una richiesta pull a Github! Il mondo ha bisogno di più azioni :) – TemplateRex

+2

Accetterei volentieri una richiesta pull per 'action :: shuffle'. Non per 'action :: random_shuffle'. –

3

L'ultima versione di git contiene già action::shuffle. Può essere utilizzato come segue:

#include <random> 
std::mt19937 gen; 
... 
v |= action::push_back(view::iota(0, N)) | action::shuffle(gen); 
+0

Tnx, lo so, l'ho provato :) – TemplateRex

Problemi correlati