J'ai appris à programmer principalement d'un OOP Standard (comme la plupart d'entre nous, je suis sûr), mais j'ai passé beaucoup de temps à essayer d'apprendre à résoudre les problèmes de manière fonctionnelle. J'ai une bonne saisie sur la façon de résoudre les problèmes de calcul avec la FP, mais quand il s'agit de problèmes plus compliqués, je me trouve toujours me retrouver que je me retrouve pour avoir besoin d'objets mutables. Par exemple, si j'écris un simulateur de particules, je voudrais "Objets" de particules avec une position mutable à mettre à jour. Comment les problèmes inhérents "stipuleux" sont-ils généralement résolus en utilisant des techniques de programmation fonctionnelle?
Les programmes fonctionnels gèrent très bien l'état, mais nécessitent une manière différente de la regarder. Pour votre exemple exemple, une chose à considérer est avoir votre position être une fonction de temps au lieu d'une valeur fixe. Cela fonctionne bien pour les particules suivant un chemin mathématique fixe, mais vous avez besoin d'une stratégie différente de manipuler une modification du chemin, telle que après une collision.
La stratégie de base ici est que vous créez des fonctions qui prennent dans un état et renvoient le nouvel état. Donc, un simulateur de particules serait une fonction qui prend un Set
de particules comme entrée et renvoie une nouvelle Set
de particules après une étape temporelle. Ensuite, vous appelez à plusieurs reprises cette fonction avec son jeu d'entrée sur son résultat précédent.
Comme indiqué par @karlbielefeldt, l'approche fonctionnelle de ce problème est de le considérer comme renvoyant un nouvel état d'un précédent état. Les fonctions elles-mêmes ne contiennent aucune information. Ils mettront donc toujours à jour l'état m à l'état N .
Je pense que vous trouvez cet inefficace parce que vous supposez que l'état précédent doit être conservé en mémoire tout en calculant le nouvel état. Notez que le choix entre écrire un tout nouvel état ou ré-réécrire l'ancien en place est un détail de mise en œuvre du point de vue d'une langue fonctionnelle .
Par exemple, disons que j'ai une liste d'un million d'entiers et que je souhaite incrémenter le dixième par une unité. Copier la liste complète avec un nouveau numéro dans sa dixième position est inutile, vous avez raison; Mais ce n'est que la manière conceptuelle de décrire l'opération au compilateur de langue ou à l'interprète. Le compilateur ou l'interprète est libre de prendre la première liste et d'écraser simplement la dixième position.
L'avantage de décrire l'opération de cette manière est que le compilateur peut raisonner sur la situation lorsque de nombreux threads veulent mettre à jour la même liste à différentes positions. Si l'opération est décrite comme "Aller à cette position et écrase ce que vous trouvez", c'est le programmeur, pas le compilateur, qui est chargé de s'assurer que les écrasements ne sont pas entrés en collision.
Avec tout ce qui dit, même à Haskell, il y a une State Monad qui aide à modéliser des situations où "garder l'état" est une solution plus intuitive pour un problème. Mais veuillez également remarquer quelques problèmes que vous trouvez " intrinsèquement état, comme écrire une base de données " ont solutions immuables telles que DATOMIC . Cela peut être surprenant jusqu'à ce que vous comprenez que c'est un concept, pas nécessairement sa réalisation.
S'abonner au Modèle mental droit aide à mieux penser et gérer l'état. Dans mon esprit, le meilleur modèle mental est le Book Book . Une fois que cela clique, vous comprendrez que FP se penche fortement sur les structures de données persistantes qui capturent l'état du monde et que les fonctions sont utilisées pour transmettre cet état sans aucune mutation.
Riche Hickey illumine ces idées:
Il - sont d'autres pourparlers mais cela devrait vous envoyer dans la bonne direction.
Lorsque vous écrivez des applications importantes et modérément importantes, j'ai souvent constaté qu'il est utile de différencier les sections de la demande d'état et de celles qui sont apatrides.
Les classes/structures de données dans la section sotculeuse stockent les données de l'application et les fonctions de cette section fonctionnent avec une connaissance implicite des données de l'application.
Les classes/structures de données/fonctions de la section apatrides sont présentes pour prendre en charge les aspects purement algorithmiques de l'application. Ils n'ont pas la connaissance implicite des données de l'application. Ils travaillent dans une nature purement fonctionnelle. Les parties stillées de l'application peuvent subir un changement d'état comme effet secondaire des fonctions d'exécution dans la section apatrides de l'application.
La partie la plus difficile consiste à déterminer quelles classes/fonctions à mettre dans la section apatrides et quelles classes/fonctions à mettre dans la section énorme et à avoir la discipline de les mettre dans des fichiers/bibliothèques distincts.