Quelle est la différence entre les langages de programmation fonctionnels et impératifs?

159

La plupart des langages traditionnels, y compris les langages de programmation orientée objet (POO) tels que C #, Visual Basic, C ++ et Java ont été conçus pour prendre principalement en charge la programmation impérative (procédurale), tandis que les langages de type Haskell / gofer sont purement fonctionnels. Quelqu'un peut-il expliquer quelle est la différence entre ces deux modes 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?

Swapnil Kotwal
la source
1
duplication possible de la programmation fonctionnelle vs programmation orientée objet
Floris Velleman
1
Vérifiez cet autre [post] [1]. Il décrit clairement les différences. [1]: stackoverflow.com/questions/602444/…
thêta

Réponses:

160

Définition: Un langage impératif utilise une séquence d'énoncés pour déterminer comment atteindre un certain objectif. On dit que ces instructions changent l'état du programme lorsque 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 instructions, le programme est explicitement dit comment additionner les nombres 5, 10 et 15 ensemble.

Langages fonctionnels: Le paradigme de 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 un certain nombre d'avantages, notamment les suivants: Meilleure lisibilité et maintenabilité. En effet, chaque fonction est conçue pour accomplir une tâche spécifique compte tenu de ses arguments. La fonction ne repose sur aucun état externe.

Développement répétitif plus facile. Le code étant plus facile à refactoriser, les modifications apportées à la conception sont souvent plus faciles à implémenter. Par exemple, supposons que vous écriviez une transformation compliquée, puis que vous vous rendiez compte 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 à volonté sans vous soucier des effets secondaires.

Tests et débogage plus faciles. Étant donné que les fonctions pures peuvent plus facilement être testées isolément, vous pouvez écrire du code de test qui appelle la fonction pure avec des valeurs typiques, des cas de bord valides et des cas de bord invalides.

Pour les personnes OOP ou les langues impératives:

Les langages orientés objet sont bons lorsque vous avez un ensemble fixe d'opérations sur les choses et 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 les classes existantes sont laissées seules.

Les langages fonctionnels sont bons lorsque vous avez un ensemble fixe de choses et que votre code évolue, vous ajoutez principalement de nouvelles opérations sur des é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 seules.

Les inconvénients:

Le choix de la méthode de programmation dépend des besoins des utilisateurs, il n'y a donc de mal que lorsque les utilisateurs ne choisissent pas la bonne manière.

Lorsque l'évolution va dans le mauvais sens, vous avez des problèmes:

  • L'ajout d'une nouvelle opération à un programme orienté objet peut nécessiter la modification de nombreuses définitions de classe pour ajouter une nouvelle méthode
  • L'ajout d'un nouveau type de chose à un programme fonctionnel peut nécessiter la modification de nombreuses définitions de fonction pour ajouter un nouveau cas.
Chris
la source
10
Une fonction pure dans ce cas est l'équivalent d'une fonction mathématique. Les mêmes entrées seront toujours mappées aux mêmes sorties. Ils manquent également d'effets secondaires (autres que de renvoyer une valeur ou des valeurs), ce qui signifie que le compilateur peut faire quelques optimisations intéressantes, et il est plus facile d'exécuter une fonction en parallèle car il n'y a rien à affronter.
WorBlux
Ainsi, les bonnes manières et les meilleures pratiques de composition d'applications oop maintenables et testables ont tendance à être de concevoir du code impératif avec un état d'esprit déclinatif?
Kemal Gültekin
4
Je ne vois pas de différence claire sur le texte où la caractéristique de chaque programmation est mise en évidence. La plupart de la description de la programmation procédurale peut être remplacée par le texte de programmation impératif et vice versa.
AxeEffect
7
Cette réponse tente de clarifier ce qu'est la programmation fonctionnelle mais ne prend même pas la peine de définir ce qu'est une fonction pure. Je ne vois pas comment quiconque pourrait lire cette réponse et en ressortir confiant de connaître la différence entre la programmation déclarative et procédurale.
Ringo
230

Voici la différence:

Impératif:

  • Début
  • Allumez vos chaussures de taille 9 1/2.
  • Faites de la place dans votre poche pour garder un tableau [7] de clés.
  • Mettez les clés dans la pièce pour les clés dans la poche.
  • Entrez dans le garage.
  • Garage ouvert.
  • Entrez dans la voiture.

... et ainsi de suite ...

  • Mettez le lait au réfrigérateur.
  • Arrêtez.

Déclaratif, dont fonctionnel est une sous-catégorie:

  • Le lait est une boisson saine, sauf si vous avez des problèmes de digestion du lactose.
  • Habituellement, on stocke le lait dans un réfrigérateur.
  • Un réfrigérateur est une boîte qui garde les objets qu'il contient au frais.
  • Un magasin est un endroit où les articles sont vendus.
  • Par «vendre», nous entendons l'échange de choses contre de l'argent.
  • En outre, l'échange d'argent contre des choses s'appelle «acheter».

... et ainsi de suite ...

  • Assurez-vous que nous avons du lait dans le réfrigérateur (quand nous en avons besoin - pour les langages fonctionnels paresseux).

Résumé: Dans les langages impératifs, vous indiquez à l'ordinateur comment changer les bits, les octets et les mots de sa mémoire et dans quel ordre. Dans les fonctions fonctionnelles, nous disons à l'ordinateur ce que sont les choses, les actions, etc. Par exemple, nous disons que la factorielle de 0 est 1, et la factorielle de tout autre nombre naturel est le produit de ce nombre par la factorielle de son prédécesseur. Nous ne disons pas: pour calculer la factorielle de n, réserver une région mémoire et y stocker 1, puis multiplier le nombre dans cette région mémoire avec les nombres 2 à n et stocker le résultat au même endroit, et à la fin, la région mémoire contiendra la factorielle.

Ingo
la source
1
Je vous remercie. C'est une excellente façon de voir les choses.
L-Samuels
5
J'ai aimé votre explication @Igno, mais quelque chose n'est toujours pas clair pour moi. Dans Declarative, même si vous dites simplement des choses, mais vous devez quand même changer les bits et apporter des modifications aux états de la machine pour procéder correctement. Cela me rend confus, que la déclaration déclarative est en quelque sorte similaire à la programmation procédurale (comme les fonctions C) , et qu'il y a toujours une grande différence entre elles en interne. Les fonctions C ne sont-elles pas identiques aux fonctions de la programmation fonctionnelle (au niveau machine)?
phoenisx
11
@Igno, comme Subroto, je ne comprends pas vraiment votre explication. Il semble que ce que vous avez écrit peut se résumer ainsi: Besoin d'une réponse ... obtenir une réponse. il semble ignorer les éléments importants, c'est comment. Je ne comprends pas comment vous pouvez simplement cacher cette partie à l'utilisateur, à un moment donné, quelqu'un doit savoir comment cela a été fait ... vous ne pouvez pas garder l'assistant derrière le rideau pour toujours.
Brett Thomas
3
Ce n'est pas à distance ce que je comprends de la programmation fonctionnelle. Je pensais que la programmation fonctionnelle était la suppression des entrées et sorties cachées des fonctions.
Ringo
7
Explication compliquée.
JoeTidee
14

La plupart des langages modernes sont à des degrés divers à la fois impératifs et fonctionnels, mais pour mieux comprendre la programmation fonctionnelle, il sera préférable de prendre un exemple de langage fonctionnel pur comme Haskell en contraste avec un code impératif dans un langage pas si fonctionnel comme java / c #. Je pense qu'il est toujours facile d'expliquer par l'exemple, alors en voici un.

Programmation fonctionnelle: calculer factorielle de n soit n! soit nx (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 fonction au niveau de la valeur de l'argument. Voici maintenant un exemple de code impératif avec un 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;
}

Cette lecture peut être une bonne référence pour comprendre comment le code impératif se concentre davantage sur la manière dont la partie, l'état de la machine (i dans la boucle for), l'ordre d'exécution, le contrôle de flux.

Le dernier exemple peut être considéré comme du code java / c # lang grossièrement et la première partie comme une limitation du langage lui-même contrairement à Haskell pour surcharger la fonction par valeur (zéro) et par conséquent, on peut dire qu'il ne s'agit pas d'un langage fonctionnel puriste, d'autre part part, vous pouvez dire qu'il prend en charge prog fonctionnel. dans une certaine mesure.

Divulgation: aucun des codes ci-dessus n'est testé / exécuté mais, espérons-le, devrait être assez bon pour transmettre le concept; J'apprécierais également des commentaires pour une telle correction :)

vieux-moine
la source
1
Cela ne devrait-il pas être le cas return n * factorial(n-1);?
jinawee
@jinawee, merci de l'avoir signalé, je l'ai corrigé den * (n-1)
old-monk
10

La programmation fonctionnelle est une forme de programmation déclarative, qui décrit la logique du calcul et l'ordre d'exécution est complètement désaccentué.

Problème: je veux changer cette créature de cheval en girafe.

  • Allonger le cou
  • Allonger les jambes
  • Appliquer des taches
  • Donnez à la créature une langue noire
  • Retirer la queue de cheval

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.

  1. Notez l'état initial de la porte de garage
  2. Arrêtez la voiture dans l'allée
  3. Si la porte de garage est fermée, ouvrez la porte de garage, rappelez-vous le nouvel état; sinon continuez
  4. Tirer la voiture dans le garage
  5. Fermer la porte de garage

Chaque étape doit être effectuée pour arriver au résultat souhaité. Tirer dans le garage alors que la porte de garage est fermée entraînerait une porte de garage cassée.

Jakub Keller
la source
Je ne vois que la différence entre async et sync.
Vladimir Vukanac
@VladimirVukanac async / sync est un mécanisme, pas une forme de programmation
Jakub Keller
2
Oh, merci, je vais chercher plus à ce sujet. Auriez-vous la gentillesse de mettre à jour le problème 1 pour qu'il soit le même que le problème 2 "Je veux garer ma voiture" mais écrit de manière fonctionnelle? Le parallélisme sera alors exclu.
Vladimir Vukanac
6

La programmation fonctionnelle est une «programmation avec des fonctions», où une fonction a certaines propriétés mathématiques attendues, y compris la transparence référentielle. De ces propriétés découlent d'autres propriétés, en particulier des étapes de raisonnement familières permises par la substituabilité qui conduisent à des preuves mathématiques (c'est-à-dire justifiant la confiance dans un résultat).

Il s'ensuit qu'un programme fonctionnel n'est qu'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 référentiellement transparente (et n'est donc pas construite avec des fonctions et des valeurs, et ne peut elle-même faire partie d'une fonction). Les deux endroits les plus évidents sont: la mutation (par exemple des variables) d'autres effets secondaires flux de contrôle non local (par exemple des exceptions)

Sur ce cadre de programmes en tant qu'expressions qui sont 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 le terme aux langages avec des abstractions spécifiquement pertinentes (telles que des fermetures, des valeurs immuables et des aides syntaxiques telles que la correspondance de modèles). En ce qui concerne l'utilisation de la programmation fonctionnelle, elle implique l'utilisation de fonctins et construit du code sans aucun effet secondaire. utilisé pour écrire des preuves

Romil Pawar
la source
3

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 qui répertorie exactement ce que notre application doit faire, étape par étape.

Le style de programmation fonctionnelle produit de l'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 sujet, 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 le comportement de l'application.

Maintenant, ces descriptions rapides n'ont probablement pas beaucoup de sens, alors passons en revue les différences entre la programmation impérative et déclarative en parcourant une analogie.

Imaginez que nous ne construisons pas de logiciels, mais que nous préparons des tartes pour gagner notre vie. Peut-être que nous sommes de mauvais boulangers et que nous ne savons pas comment préparer une délicieuse tarte comme nous le devrions.

Alors notre patron nous donne une liste de directions, ce que nous appelons une recette.

La recette nous dira comment faire une tarte. Une recette est écrite dans un style impératif comme ceci:

  1. Mélanger 1 tasse de farine
  2. Ajouter 1 œuf
  3. Ajouter 1 tasse de sucre
  4. Versez le mélange dans une casserole
  5. Mettez la casserole au four pendant 30 minutes et 350 degrés F.

La recette déclarative ferait ce qui suit:

1 tasse de farine, 1 œuf, 1 tasse de sucre - état initial

Règles

  1. Si tout est mélangé, placez-le dans le moule.
  2. Si tout n'est pas mélangé, placez-le dans un bol.
  3. Si tout est dans le moule, placez-le au four.

Les approches impératives sont donc caractérisées par des approches étape par étape. Vous commencez par la première étape et passez à l'étape 2 et ainsi de suite.

Vous vous retrouvez finalement avec un produit final. Donc, pour faire cette tarte, nous prenons ces ingrédients, les mélangons, 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, en commençant par une partie qui répertorie l'état initial de la recette, comme les variables. Donc nos variables ici sont 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 ayons une tarte à la rhubarbe et aux fraises prête à manger ou autre.

Donc, dans une approche déclarative, il faut savoir structurer correctement ces règles.

Donc, les règles que nous pourrions vouloir examiner nos ingrédients ou indiquer, si elles sont mélangées, les mettre 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.

Maintenant, nous avons un bol d'ingrédients mélangés comme notre état.

Maintenant, nous appliquons à nouveau ce nouvel état à nos règles.

Donc, la règle 1 dit que si les ingrédients sont mélangés, placez-les dans une casserole, d'accord ouais maintenant la règle 1 s'applique, faisons-le.

Maintenant, nous avons ce nouvel état où les ingrédients sont mélangés et dans une casserole. La règle 1 n'est plus pertinente, la règle 2 ne s'applique pas.

La règle 3 dit que si les ingrédients sont dans une casserole, placez-les dans le four, c'est bien cette règle qui s'applique à ce nouvel état, faisons-le.

Et nous nous retrouvons avec une délicieuse tarte aux pommes chaude ou autre.

Maintenant, si vous êtes comme moi, vous vous demandez peut-être pourquoi ne faisons-nous pas encore 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.

  1. Si un utilisateur entre du texte, mettez à jour l'entrée de texte. Eh bien, pour le moment, cela ne s'applique pas.

  2. Si le modèle est rendu, calculez le widget.

  3. Si textInput est mis à jour, restaurez le modèle.

Eh bien, rien de tout cela ne s'applique, donc le programme attendra juste qu'un événement se produise.

Ainsi, à un moment donné, un utilisateur met à jour la saisie de texte, puis nous pourrions appliquer la règle numéro 1.

Nous pouvons mettre à jour cela pour “abcd”

Nous venons donc de mettre à jour nos mises à jour de texte et de textInput, la règle numéro 2 ne s'applique pas, la règle numéro 3 dit que si l'entrée de texte est mise à jour, ce qui vient de se produire, puis restituer le modèle, puis nous revenons à la règle 2 qui dit si le modèle est rendu , calculez le widget, d'accord, calculons le widget.

En général, en tant que programmeurs, nous voulons nous efforcer d'obtenir des conceptions de programmation plus déclaratives.

L'impératif semble plus clair et évident, mais une approche déclarative s'adapte très bien aux applications plus importantes.

Daniel
la source
2

• Langues impératives:

  • Exécution efficace

  • Sémantique complexe

  • Syntaxe complexe

  • La concurrence est conçue par un programmeur

  • Les tests complexes, n'ont pas de transparence référentielle, ont des effets secondaires

  • A l'état

• Langages fonctionnels:

  • Sémantique simple

  • Syntaxe simple

  • Exécution moins efficace

  • Les programmes peuvent être automatiquement créés simultanément

  • Des tests simples, ont une transparence référentielle, n'ont pas d'effets secondaires

  • N'a pas d'état
BoraKurucu
la source
1

Je pense qu'il est possible d'exprimer la programmation fonctionnelle de manière impérative:

  • Utilisation de nombreuses vérifications d'état des objets et if... else/ switchinstructions
  • Un mécanisme de temporisation / d'attente pour prendre en charge l'asynchronisme

Il y a d'énormes problèmes avec une telle approche:

  • Les règles / procédures sont répétées
  • L'état laisse des risques d'effets secondaires / d'erreurs

La programmation fonctionnelle, traitant les fonctions / méthodes comme des objets et embrassant l'apatridie, est née pour résoudre ces problèmes que je crois.

Exemple d'utilisations: applications frontales comme Android, iOS ou logiques d'applications Web incl. communication avec le backend.

Autres défis lors de la simulation de la programmation fonctionnelle avec du code impératif / procédural:

  • Condition de course
  • Combinaison complexe et séquence d'événements. Par exemple, l'utilisateur essaie d'envoyer de l'argent dans une application bancaire. Étape 1) Faites tout ce qui suit en parallèle, ne procédez que si tout est bon a) Vérifiez si l'utilisateur est toujours bon (fraude, AML) b) vérifiez si l'utilisateur a un solde suffisant c) Vérifiez si le destinataire est valide et bon (fraude, AML) etc. Étape 2) effectuer l'opération de transfert Étape 3) Afficher la mise à jour du solde de l'utilisateur et / ou une sorte de suivi. Avec RxJava par exemple, le code est concis et sensé. Sans cela, je peux imaginer qu'il y aurait beaucoup de code, de code désordonné et sujet aux erreurs

Je crois aussi qu'en fin de compte, le code fonctionnel sera traduit en assemblage ou en code machine, ce qui est impératif / procédural par les compilateurs. Cependant, à moins que vous n'écriviez l'assembly, en tant qu'êtres humains écrivant du code avec un langage de haut niveau / lisible par l'homme, la programmation fonctionnelle est le moyen d'expression le plus approprié pour les scénarios répertoriés

Ericn
la source
-1

Je sais que cette question est plus ancienne et que d'autres l'ont déjà bien expliqué, je voudrais donner un exemple de problème qui explique la même chose en termes simples.

Problème: Ecrire la table des 1.

Solution: -

Par style impératif: =>

    1*1=1
    1*2=2
    1*3=3
    .
    .
    .
    1*n=n 

Par style fonctionnel: =>

    1
    2
    3
    .
    .
    .
    n

Explication en style impératif, nous écrivons les instructions plus explicitement et qui peuvent être appelées de manière plus simplifiée.

Où, comme dans le style fonctionnel, les choses qui s'expliquent d'elles-mêmes seront ignorées.

Param NC
la source