La plupart des langages traditionnels, y compris les langages de programmation orientée objet tels que C #, Visual Basic, C++ et Java, étaient principalement conçus pour prendre en charge la programmation impérative (procédurale), alors que les langages analogues à Haskell/gofer sont purement fonctionnels. Quelqu'un peut-il préciser quelle est la différence entre ces deux méthodes de programmation?
Je sais que le choix du mode de programmation dépend des besoins des utilisateurs, mais pourquoi est-il recommandé d'apprendre des langages de programmation fonctionnels?
Définition: Un langage impératif utilise une séquence d'instructions pour déterminer comment atteindre un objectif donné. On dit que ces instructions changent l’état du programme au fur et à mesure que chacune est exécutée à son tour.
Exemples: Java est un langage impératif. Par exemple, un programme peut être créé pour ajouter une série de nombres:
int total = 0;
int number1 = 5;
int number2 = 10;
int number3 = 15;
total = number1 + number2 + number3;
Chaque instruction modifie l'état du programme, de l'attribution de valeurs à chaque variable à l'ajout final de ces valeurs. En utilisant une séquence de cinq énoncés, on explique explicitement au programme comment additionner les chiffres 5, 10 et 15.
Langages fonctionnels: Le paradigme de la programmation fonctionnelle a été explicitement créé pour prendre en charge une approche purement fonctionnelle de la résolution de problèmes. La programmation fonctionnelle est une forme de programmation déclarative.
Avantages des fonctions pures: .__ La principale raison d'implémenter des transformations fonctionnelles en tant que fonctions pures est que les fonctions pures sont composables: c'est-à-dire autonomes et sans état. Ces caractéristiques apportent de nombreux avantages, dont les suivants: Lisibilité et maintenabilité accrues. En effet, chaque fonction est conçue pour accomplir une tâche spécifique en fonction de ses arguments. La fonction ne repose sur aucun état externe.
Développement réitératif plus facile. Comme le code est plus facile à refactoriser, les modifications de conception sont souvent plus faciles à implémenter. Par exemple, supposons que vous écriviez une transformation compliquée, puis réalisiez que du code est répété plusieurs fois dans la transformation. Si vous refactorisez par une méthode pure, vous pouvez appeler votre méthode pure à votre guise, sans vous soucier des effets secondaires.
Faciliter les tests et le débogage. Etant donné que les fonctions pures peuvent plus facilement être testées isolément, vous pouvez écrire un code de test qui appelle la fonction pure avec des valeurs typiques, des cas Edge valides et des cas Edge non valides.
Pour OOP Personnes ou Langues impératives:
Les langages orientés objet sont utiles lorsque vous avez un ensemble d'opérations fixes sur les choses et que, à mesure que votre code évolue, vous ajoutez principalement de nouvelles choses. Cela peut être accompli en ajoutant de nouvelles classes qui implémentent des méthodes existantes et en laissant les classes existantes seules.
Les langages fonctionnels sont utiles lorsque vous avez un ensemble d'éléments fixe et que, à mesure que votre code évolue, vous ajoutez principalement de nouvelles opérations sur les éléments existants. Cela peut être accompli en ajoutant de nouvelles fonctions qui calculent avec les types de données existants et les fonctions existantes sont laissées à elles-mêmes.
Les inconvénients:
Le choix du mode de programmation dépend des besoins des utilisateurs. Il n’ya donc de préjudice que lorsque les utilisateurs ne choisissent pas le moyen approprié.
Lorsque l'évolution va dans le mauvais sens, vous avez des problèmes:
Voici la différence:
Impératif:
... et ainsi de suite ...
Déclaratif, dont fonctionnel est une sous-catégorie:
... et ainsi de suite ...
Résumé: Dans les langues impératives, vous indiquez à l'ordinateur comment modifier les bits, les octets et les mots de sa mémoire et dans quel ordre. Dans les fonctions, nous disons à l'ordinateur quelles sont les choses, les actions, etc. Par exemple, nous disons que la factorielle de 0 est 1 et que la factorielle de tout autre nombre naturel est le produit de ce nombre et la factorielle de son prédécesseur. Nous ne disons pas: pour calculer la factorielle de n, réserver une région de mémoire et y stocker 1, puis multiplier le nombre dans cette région de mémoire par les nombres 2 à n et stocker le résultat au même endroit et à la fin, la région mémoire contiendra la factorielle.
Programmation fonctionnelle est une forme de programmation déclarative qui décrit la logique de calcul et dont l’ordre d’exécution est totalement réduit.
Problème: je veux changer cette créature de cheval en girafe.
Chaque élément peut être exécuté dans n'importe quel ordre pour produire le même résultat.
La programmation impérative est procédurale. L'état et l'ordre sont importants.
Problème: je veux garer ma voiture.
Chaque étape doit être effectuée pour arriver au résultat souhaité. Entrer dans le garage alors que la porte du garage est fermée provoquerait une porte cassée.
La plupart des langages modernes sont, à des degrés divers, à la fois impératifs et fonctionnels, mais pour mieux comprendre la programmation fonctionnelle, il est préférable de prendre un exemple de langage purement fonctionnel, tel que Haskell, par opposition au code impératif dans un langage moins fonctionnel, tel que Java/c #. Je crois qu'il est toujours facile d'expliquer par l'exemple, donc ci-dessous en est un.
Programmation fonctionnelle: calcul de factorielle de n i.e n! c'est-à-dire n x (n-1) x (n-2) x ... x 2 X 1
-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution
factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3
-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1
-- | 3 x (2 x (1 x (1)) = 6
Notez que Haskel autorise la surcharge de la fonction au niveau de la valeur de l’argument. Voici ci-dessous un exemple de code impératif à degré croissant d’impérativité:
//somewhat functional way
function factorial(n) {
if(n < 1) {
return 1;
}
return n * factorial(n-1);
}
factorial(3);
//somewhat more imperative way
function imperativeFactor(n) {
int f = 1
for(int i = 1; i <= n; i++) {
f = f * i
}
return f;
}
Ceci read peut être une bonne référence pour comprendre que l’impératif du code se concentre davantage sur la manière dont la partie, l’état de la machine (i dans la boucle), l’ordre d’exécution, le contrôle de flux.
Le dernier exemple peut être considéré comme un code Java/c # lang approximativement et une première partie comme une limitation du langage lui-même, contrairement à Haskell pour surcharger la fonction par une valeur (zéro) et peut donc être considéré comme un langage fonctionnel non puriste vous pouvez dire qu'il supporte le prog fonctionnel. dans une certaine mesure.
Divulgation: aucun des codes ci-dessus n'a été testé/exécuté, mais devrait être assez bon pour transmettre le concept; J'apprécierais aussi les commentaires pour une telle correction :)
La programmation fonctionnelle est une "programmation avec des fonctions", dans laquelle une fonction possède certaines propriétés mathématiques attendues, notamment la transparence référentielle. D'autres propriétés découlent de ces propriétés, en particulier des étapes de raisonnement bien connues, rendues possibles par une substituabilité conduisant à des preuves mathématiques (c'est-à-dire justifiant la confiance dans un résultat).
Il s'ensuit qu'un programme fonctionnel est simplement une expression.
Vous pouvez facilement voir le contraste entre les deux styles en notant les endroits dans un programme impératif où une expression n'est plus transparente de manière référentielle (et n'est donc pas construite avec des fonctions et des valeurs, et ne peut pas elle-même faire partie d'une fonction). Les deux endroits les plus évidents sont: Mutation (par exemple, variables) Autres effets indésirables Flux de contrôle non local (par exemple, exceptions)
Sur ce cadre de programmes-comme-expressions, composés de fonctions et de valeurs, est construit tout un paradigme pratique de langages, de concepts, de "modèles fonctionnels", de combinateurs et de divers systèmes de types et algorithmes d'évaluation.
Selon la définition la plus extrême, presque tous les langages, même C ou Java, peuvent être qualifiés de fonctionnels, mais les gens réservent généralement ce terme à des abstractions spécifiques (telles que des fermetures, des valeurs immuables et des aides syntaxiques telles que le filtrage par motif) .. En ce qui concerne l’utilisation de la programmation fonctionnelle, elle implique l’utilisation de fonctions et construit du code sans aucun effet secondaire . Utilisé pour écrire des preuves
Le style de programmation impératif a été pratiqué dans le développement Web de 2005 à 2013.
Avec la programmation impérative, nous avons écrit du code décrivant exactement ce que notre application devrait faire, étape par étape.
Le style de programmation fonctionnel produit une abstraction grâce à des méthodes intelligentes de combinaison de fonctions.
Il est fait mention de la programmation déclarative dans les réponses et à ce propos, je dirai que la programmation déclarative énumère certaines règles que nous devons suivre. Nous fournissons ensuite ce que nous appelons un état initial à notre application et nous laissons ces règles définir en quelque sorte le comportement de l'application.
Maintenant, ces descriptions rapides n’ont probablement pas beaucoup de sens, aussi, passons en revue les différences entre la programmation impérative et la programmation déclarative en passant par une analogie.
Imaginez que nous ne construisions pas de logiciel, mais que nous cuisinions des tartes pour gagner notre vie. Peut-être sommes-nous de mauvais boulangers et ne savons pas comment faire une tarte aussi délicieuse que nous le devrions.
Donc, notre patron nous donne une liste de directions, ce que nous savons comme une recette.
La recette nous dira comment faire une tarte. Une recette est écrite dans un style impératif comme ceci:
La recette déclarative ferait ce qui suit:
1 tasse de farine, 1 œuf, 1 tasse de sucre - État initial
Règles
Les approches impératives sont donc caractérisées par des approches pas à pas. Vous commencez par la première étape et passez à l’étape 2 et ainsi de suite.
Vous finissez par vous retrouver avec un produit final. Donc, pour faire cette tarte, nous prenons ces ingrédients, nous les mélangeons, nous les mettons dans une casserole et au four et vous obtenez votre produit final.
Dans un monde déclaratif, c'est différent. Dans la recette déclarative, nous séparerions notre recette en deux parties distinctes, commençant par une partie qui répertorie l'état initial de la recette, comme les variables. Nos variables ici sont donc les quantités de nos ingrédients et leur type.
Nous prenons l’état initial ou les ingrédients initiaux et leur appliquons certaines règles.
Nous prenons donc l’état initial et les passons à travers ces règles encore et encore jusqu’à ce que nous obtenions une tarte à la fraise à la rhubarbe prête à l’emploi ou autre.
Donc, dans une approche déclarative, nous devons savoir comment structurer correctement ces règles.
Donc, les règles que nous pourrions vouloir examiner nos ingrédients ou notre état, si mélangées, les mettent dans une casserole.
Avec notre état initial, cela ne correspond pas car nous n’avons pas encore mélangé nos ingrédients.
Donc, la règle 2 dit: s'ils ne sont pas mélangés, mélangez-les dans un bol. Ok ouais cette règle s'applique.
Nous avons maintenant un bol d'ingrédients mélangés dans notre état.
Maintenant, nous appliquons à nouveau ce nouvel état à nos règles.
Ainsi, la règle 1 dit que si les ingrédients sont mélangés, placez-les dans un plat, d'accord ouais, maintenant la règle 1 s'applique, faisons-le.
Nous avons maintenant ce nouvel état où les ingrédients sont mélangés et placés dans une casserole. La règle 1 n'est plus pertinente, la règle 2 ne s'applique pas.
La règle 3 stipule que si les ingrédients sont dans une casserole, placez-les dans le four. La règle est ce qui s'applique à ce nouvel état, faisons-le.
Et nous nous retrouvons avec une délicieuse tarte aux pommes ou autre chose.
Maintenant, si vous êtes comme moi, vous vous demandez peut-être pourquoi nous ne faisons toujours pas de programmation impérative. C'est logique.
Eh bien, pour les flux simples, oui, mais la plupart des applications Web ont des flux plus complexes qui ne peuvent pas être correctement capturés par une conception de programmation impérative.
Dans une approche déclarative, nous pouvons avoir des ingrédients initiaux ou un état initial comme textInput=“”
, une seule variable.
Peut-être que la saisie de texte commence par une chaîne vide.
Nous prenons cet état initial et l'appliquons à un ensemble de règles définies dans votre application.
Si un utilisateur entre du texte, mettez à jour la saisie de texte. En ce moment, cela ne s’applique pas.
Si le modèle est rendu, calculez le widget.
Eh bien, rien de tout cela ne s'applique, le programme attendra simplement qu'un événement se produise.
Ainsi, à un moment donné, un utilisateur met à jour la saisie de texte et nous pouvons ensuite appliquer la règle numéro 1.
Nous pouvons mettre à jour cela en “abcd”
Donc, nous venons de mettre à jour nos mises à jour de texte et textInput, la règle numéro 2 ne s'applique pas, la règle numéro 3 indique que si l'entrée de texte est mise à jour, ce qui vient juste de se produire, puis restituons le modèle, puis nous revenons à la règle 2 qui indique que le modèle est rendu. , calculer le widget, d'accord permet de calculer le widget.
En général, en tant que programmeurs, nous souhaitons développer davantage de conceptions de programmation déclaratives.
L'impératif semble plus clair et plus évident, mais une approche déclarative s'adapte très bien pour les applications plus grandes.
Je pense qu'il est possible d'exprimer la programmation fonctionnelle de manière impérative:
- Utilisation de nombreux contrôles d'état d'objets et d'instructions if... else
/switch
- Un mécanisme de délai d’attente/attente pour prendre en charge les asynchromes
Une telle approche pose d'énormes problèmes:
- Les règles/procédures sont répétées - Statefulness laisse des risques d'effets secondaires/erreurs
La programmation fonctionnelle, traitant des fonctions/méthodes comme des objets et englobant l’apatridie, est née pour résoudre ces problèmes.
Exemple d'utilisations: applications front-end telles qu'Android, iOS ou les logiques d'applications Web incl. communication avec backend