IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Template metaprogramming et fonctions du second ordre


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Ingénieur géomaticien
    Inscrit en
    Juillet 2015
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur géomaticien

    Informations forums :
    Inscription : Juillet 2015
    Messages : 34
    Par défaut Template metaprogramming et fonctions du second ordre
    Bonjour à tous !

    Je m'excuse par avance de ce post qui risque d'être long, et plutôt technique... Elle s'adresse plutôt aux fans de la programmation fonctionnelle .

    J'essaie de reproduire un simulacre de Blitz++, permettant d'effectuer des calculs vectoriels à la sauce Matlab / J.
    Je pense avoir bien compris l'intérêt des template expressions permettant de regrouper les boucles for et ainsi éviter les allocations de données superflus qui plombent la RAM.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    template<typename Left, typename Op, typename Right>
    struct X {
       Left leftNode_;
       Right rightNode_;
     
       X(Left t1, Right t2)
       : leftNode_(t1), rightNode_(t2)
       { }
     
       // The trick: delegate the [] operator to subexpressions, recursively
       double operator[](int i)
       {
          return Op::apply(leftNode_[i],rightNode_[i]);
       }
    };
     
    // Simple array class (fixed size)
    struct Array {
       Array(double* data, int N)
       : data_(data), N_(N)
       { }
     
       // Assign an expression to the array
       // It is only at that time than evaluation occurs
       template<typename Left, typename Op, typename Right>
       void operator=(X<Left,Op,Right> expression)
       {
       for (int i=0; i < N_; ++i)
          data_[i] = expression[i];
       }
     
       // Access element
       double operator[](int i)
       {
          return data_[i];
       }
       double* data_;
       int N_;
    };
     
    // Operator +
    template<typename Left>
    X<Left, plus, Array> operator+(Left a, Array b)
    {
    return X<Left, plus, Array>(a,b);
    }
     
    // Tests
    int main()
    {
    double a_data[] = { 2, 3, 5, 9 },
    b_data[] = { 1, 0, 0, 1 },
    c_data[] = { 3, 0, 2, 5 },
    d_data[4];
    Array A(a_data,4), B(b_data,4), C(c_data,4), D(d_data,4);
    D = A + B + C;
    for (int i=0; i < 4; ++i)
    cout << D[i] << " "; // Output --> 6 3 7 15 
    cout << endl;
    return 0;
    }
    (exemple extrait du livre Techniques for Scientific C++, Todd Veldhuizen)

    Je me pose maintenant la question de savoir comment introduire dans ce schéma les fonctions du second ordre, comme la réduction, qui prend une fonction et renvoie une fonction dérivée. J'ai déjà réalisé un modèle qui permettait de le faire :
    Une fonction du premier ordre prend un vector (ou array) ou deux vectors et retourne un vector.
    Si elle prend un vector alors elle est monadique, si elle en prend deux elle est dyadique.

    Une fonction du second ordre prend une fonction du premier ordre et en renvoie une fonction du premier ordre dérivée. Cette fonction dérivée peut potentiellement prendre un argument (monadique), ou bien deux (dyadique).

    "plus" est une fonction du premier ordre, la réduction une fonction du second ordre.

    En C++14 (merci à jo_link_noir pour ton aide !)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    #include <iostream>
    #include <vector>
    #include <array>
     
     
    using namespace std;
     
     
    struct Function {
        constexpr Function() noexcept {}
    };
     
     
    struct Function_1st_Order : Function {
        const bool neutral;
        constexpr Function_1st_Order(bool neutral) noexcept :  Function(), neutral(neutral) {}
    };
     
    struct Add : Function_1st_Order {
     
      constexpr Add() noexcept : Function_1st_Order(0) {};
     
      /* Monad : add(a) = a*/
      template<class T>
      auto operator()(const vector<T> & right) const {
        return right;
      }
     
      /* Dyad : add(a, b) = a + b */
      template<class T, class U>
      auto operator()(const vector<T> & right, const vector<U> & left) const {
        size_t n = right.size();
        typedef typename common_type<T, U>::type C;
        vector<C> res(n);
        for(unsigned int i=0; i<n; i++) {
           res[i] = left[i] + right[i];
        }
        return res;
      }
     
    };
     
    template<typename M, typename D>
    struct Derived_Function_1st_Order : Function_1st_Order {
        const M m; /* Monadic use */
        const D d; /* Dyadic use */
     
        Derived_Function_1st_Order(bool neutral, M m, D d) noexcept :  Function_1st_Order(neutral), m(m), d(d) {}
        template<class T>
        auto operator()(const vector<T> & right) const {return m(right);};
     
        template<class T, class U>
        auto operator()(const vector<T> & right, const vector<U> & left) const {return d(right, left);};
    };
     
     
    struct Function_2nd_Order : Function {
      constexpr Function_2nd_Order() noexcept : Function() {};
    };
     
    struct Reduce : Function_2nd_Order {
      constexpr Reduce() noexcept : Function_2nd_Order() {}
      template<class F> // Replace function. Mandatory to avoid virtual template fonction declaration in class function, which is impossible
      constexpr auto operator()(const F & f) const {
     
        auto monad = [f](const auto & cont) {
          auto res = decltype(cont){f.neutral}; // Copy type of cont, without the flags
          size_t n = cont.size();
          for(int i=n-1; i>=0; i--) {
               auto cur = decltype(cont){cont[i]}; // Copy type of cont, without the flags
               res = f(res, cur);
          }
          return res[0];
        };
     
        auto dyad = [f]() {
            // Undefined for the moment
        };
     
        return Derived_Function_1st_Order<decltype(monad), decltype(dyad)>(0, monad, dyad);
      }
     
     
    };
     
    namespace {
        constexpr auto const & reduce = Reduce();
        constexpr auto const & add = Add();
    }
     
    int main()
    {
        auto cumulate = reduce(add);
        vector<int> l = {1,2,3};
        cout << cumulate(l);
        return 0;
    }
    Comment traduire ça en template expression ?
    Je prends le symbole / pour la réduction, qui prend son argument-fonction à gauche (syntaxe J).
    Pour les classes template, j'appelle :
    X un nœud d'application d'une fonction du 1er ordre, en mode dyadique : X<Left, Function, Right>
    Y un nœud d'application d'une fonction du 1er ordre, en mode monadique : Y<Function, Right>
    Z un nœud d'application d'une fonction du 2ème ordre, en mode monadique : Z<Left, Function>
    Je sais qu'un expression du type C=A+(+/)B, avec A, B et C des tableaux, pourrait être parsée ainsi :
    • C= A + Z<plus, reduce>() B
    • C= A + Y<Z<plus, reduce>, array>()
    • C= Z<array, Y<Z<plus, reduce>, array>()


    Comment effectuer l'évaluation paresseuse pour pouvoir affecter C à la dernière ligne ? Que mettre dans la classe Z ?

    Auriez-vous des idées, ne serait-ce qu'un début de piste que je pourrais creuser ? Je sais que ce que je demande est plutôt compliqué, mais je n'ai trouvé aucune référence en la matière, et pour le moment je sèche lamentablement...

    Merci beaucoup !

    Jean

    PS : En effet, en me relisant, c'est long et technique ...

  2. #2
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    Pour avoir du lazy, il faudrait que cumulate(l) retourne une expression lazy<M, std::vector<int>&> et faire un operateur+(T, lazy<M,V>) en conséquence.

    Ce qui sera compliqué avec le DSL est la partie optimisation des calculs (réordonner, pré-calculer les sorties de reduce puis évaluer les containers).

Discussions similaires

  1. Réponses: 2
    Dernier message: 07/04/2011, 15h41
  2. Réponses: 2
    Dernier message: 22/11/2007, 14h58
  3. Equation différentielle du second ordre
    Par moustiqu3 dans le forum MATLAB
    Réponses: 1
    Dernier message: 21/05/2007, 09h38
  4. Réponses: 3
    Dernier message: 22/11/2006, 21h10
  5. Template d'une fonction (!= classe)
    Par Rodrigue dans le forum Langage
    Réponses: 7
    Dernier message: 08/05/2006, 22h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo