web-dev-qa-db-fra.com

Programmation fonctionnelle en C++

Quelqu'un peut-il me guider comment faire la programmation fonctionnelle en C++? Y at-il du bon matériel en ligne que je peux consulter?

Veuillez noter que je connais la bibliothèque FC++. Je veux savoir comment faire cela avec la bibliothèque standard C++ seule.

Merci.

50
Red Hyena

Mise à jour d'août 2014: cette réponse a été publiée en 2009. C++ 11 a considérablement amélioré la gestion de la programmation fonctionnelle en C++, de sorte que cette réponse n'est plus exacte. Je le laisse ci-dessous pour un enregistrement historique.

Puisque cette réponse est restée la même, je la transforme en un wiki de communauté. N'hésitez pas à l'améliorer en collaboration pour ajouter de véritables conseils sur la programmation de fonctions avec le C++ moderne.


Vous ne pouvez pas faire vrai programmation fonctionnelle en C++. Tout ce que vous pouvez faire est d’en faire une approximation avec beaucoup de douleur et de complexité (bien que C++ 11 soit un peu plus facile). Par conséquent, cette approche n'est pas recommandée. C++ supporte relativement bien les autres paradigmes de programmation, et IMHO ne devrait pas être soumis aux paradigmes qu'il supporte moins bien - à la fin, il rendra du code illisible que seul l'auteur comprend.

23
Eli Bendersky

Vous pouvez réaliser une quantité surprenante de style de "programmation fonctionnelle" avec le C++ moderne. En fait, le langage a évolué dans cette direction depuis sa «normalisation». 

La bibliothèque standard contient des algorithmes analogues à mapper, réduire, etc. (for_each, transform, adjacent_sum ...). La révision suivante, C++ 0x, contient de nombreuses fonctionnalités conçues pour permettre aux programmeurs de les utiliser dans un style plus fonctionnel (expressions lambda, etc.).

Regardez dans les différentes bibliothèques Boost pour plus de plaisir. Juste pour illustrer le fait que le C++ standard contient beaucoup de qualités fonctionnelles, voici une fonction factorielle dans le style de passage continu en C++ standard.

#include <iostream>

// abstract base class for a continuation functor
struct continuation {
    virtual void operator() (unsigned) const = 0;
};

// accumulating continuation functor
struct accum_cont: public continuation {
    private:
        unsigned accumulator_;
        const continuation &enclosing_;
    public:
        accum_cont(unsigned accumulator, const continuation &enclosing)
            : accumulator_(accumulator), enclosing_(enclosing) {}; 
        virtual void operator() (unsigned n) const {
            enclosing_(accumulator_ * n);
        };
};

void fact_cps (unsigned n, const continuation &c)
{
    if (n == 0)
        c(1);
    else
        fact_cps(n - 1, accum_cont(n, c));
}

int main ()
{
    // continuation which displays its' argument when called
    struct disp_cont: public continuation {
        virtual void operator() (unsigned n) const {
            std::cout << n << std::endl;
        };
    } dc;

    // continuation which multiplies its' argument by 2
    // and displays it when called
    struct mult_cont: public continuation {
        virtual void operator() (unsigned n) const {
            std::cout << n * 2 << std::endl;
        };
    } mc;

    fact_cps(4, dc); // prints 24
    fact_cps(5, mc); // prints 240

    return 0;
}

Ok, j'ai menti un peu. C'est un factoriel {foncteur} _. Après tout, les fermetures sont des objets du pauvre… et vice versa. La plupart des techniques fonctionnelles utilisées en C++ reposent sur l'utilisation de foncteurs (c'est-à-dire des objets fonction): vous le verrez de manière détaillée dans la STL.

45
Derrick Turk

UPD déc 2018

J'ai créé une liste complète de matériaux dans lesquels vous pouvez trouver des articles, des conférences, des screencasts, des articles, des bibliothèques et des vitrines:

Programmation fonctionnelle en C++


Considérez mes 4 projets de recherche:

Ce projet est un prototype fonctionnel du jeu "Amber". Le code présente de nombreux concepts fonctionnels majeurs: immutability, lambdas, monads, combinators, pure functions, declarative code design. Il utilise les fonctionnalités Qt C++ et C++ 11.

Pour un exemple rapide, voyez comment les tâches peuvent être chaînées en une grosse tâche qui modifiera le monde d'Amber lorsqu'il sera appliqué:

const AmberTask tickOneAmberHour = [](const amber::Amber& amber)
{
    auto action1Res = magic::anyway(inflateShadowStorms, magic::wrap(amber));
    auto action2Res = magic::anyway(affectShadowStorms, action1Res);
    auto action3Res = magic::onFail(shadowStabilization, action2Res);
    auto action4Res = magic::anyway(tickWorldTime, action3Res);
    return action4Res.amber;
};

Ceci est une vitrine des objectifs fonctionnels génériques en C++. L'implémentation est construite avec l'utilisation de Variadic Templates, quelques piratages C++ intéressants (et valides) pour rendre les objectifs composables et esthétiques. La bibliothèque est juste une démonstration pour la conversation et ne fournit donc que quelques-uns des combinateurs les plus importants, à savoir: set(), view(), traverse(), bind(), infixer le combinateur littéral to, over() et autres.

(Notez qu'il existe les "objectifs C++" projet : mais il ne s'agit pas de véritables "objectifs", il s'agit de propriétés de classe avec des getters et des setters au sens des propriétés C # ou Java.)

Exemple rapide

Car car1 = {"x555xx", "Ford Focus", 0, {}};
Car car2 = {"y555yy", "Toyota Corolla", 10000, {}};

std::vector<Car> cars = {car1, car2};

auto zoomer = traversed<Car>() to modelL();

std::function<std::string(std::string)> variator = [](std::string) { return std::string("BMW x6"); };
std::vector<Car> result = over(zoomer, cars, variator);

QVERIFY(result.size() == 2);
QVERIFY(result[0].model == "BMW x6");
QVERIFY(result[1].model == "BMW x6");

Vous avez probablement entendu parler de monades. Les monades sont partout en discussion sur la programmation fonctionnelle maintenant. C'est un mot à la mode. Mais qu'en est-il des comonads? J'ai présenté aux automates celullaires 1D et 2D le concept de comonads sous le capot. Le but était de montrer à quel point il est facile de passer du code à flux unique au code parallèle en utilisant std :: future comme monarque. Le projet analyse et compare également deux de ces approches.

Exemple rapide

template <typename A, typename B>
UUB fmap(
    const func<B(UUA)>& f,
    const UUUUA& uuu)
{
    const func<UB(UUUA)> f2 = [=](const UUUA& uuu2)
    {
        UB newUt;
        newUt.position = uuu2.position;
        newUt.field = fp::map(f, uuu2.field);
        return newUt;
    };

    return { fp::map(f2, uuu.field), uuu.position };
}

Cette bibliothèque est basée sur Free monad et quelques autres idées avancées de programmation fonctionnelle. Son interface est similaire à la bibliothèque STM native de Haskell. Les transactions sont monadiquement composables, purement fonctionnelles et il existe de nombreux combinateurs monadiques utiles pour rendre la conception de modèle concurrent plus pratique et plus puissante. J'ai implémenté le problème de Dining Philosophers en utilisant la bibliothèque, et cela fonctionne bien. Voici un exemple de transaction pour la prise de fourchettes par un philosophe:

STML<Unit> takeFork(const TFork& tFork) {
    return withTVar<Fork, Unit>(tFork, [=](const Fork& fork) {
       if (fork.state == ForkState::Free) {
           return writeTVar<Fork>(tFork, Fork {fork.name, ForkState:Taken});
       }
       else {
           return retry<Unit>();
       }
    });
}

STML<Unit> takeForks(const TForkPair& forks) {
    STML<Unit> lm = takeFork(forks.left);
    STML<Unit> rm = takeFork(forks.right);
    return sequence(lm, rm);
}
7
graninas

Je ne pense pas que vous ne pouvez pas à la programmation réelle, réelle et fonctionnelle en C++; mais ce n'est certainement pas la façon la plus facile ou naturelle de l'utiliser. En outre, vous pouvez simplement utiliser quelques idiomes fonctionnels et non l’ensemble de l’état d’esprit (par exemple, le "style fluide").

Mon conseil serait d’apprendre un langage fonctionnel, peut-être d’abord Scheme, puis de passer à Haskell. Puis utilisez ce que vous avez appris lors de la programmation en C++. peut-être que vous n'utiliserez pas un style fonctionnel évident; mais vous pourriez obtenir les plus gros avantages (c’est-à-dire utiliser des structures immuables).

5
Javier

Il existe un livre intitulé Functional C de Pieter Hartel et Henk Muller qui pourrait aider. Si toujours disponible. Un lien vers des informations à ce sujet est ici . IIRC ce n'était pas trop mal.

1
rvirding

Probablement un peu tard, mais pour tous ceux qui cherchent, j’utilise Lua comme extension de programmation fonctionnelle en C++ et c’est génial. lua

0
daven11