web-dev-qa-db-fra.com

Différence entre la programmation logique et la programmation fonctionnelle

J'ai lu de nombreux articles en essayant de comprendre la différence entre la programmation fonctionnelle et la programmation logique, mais la seule déduction que j'ai pu faire jusqu'à présent est que la programmation logique définit les programmes à travers des expressions mathématiques. Mais une telle chose n'est pas associée à la programmation logique.

J'apprécierais vraiment un peu de lumière sur la différence entre la programmation fonctionnelle et la programmation logique.

65
Heshan Perera

Je ne dirais pas que la programmation logique définit des programmes à travers des expressions mathématiques; cela ressemble plus à une programmation fonctionnelle. La programmation logique utilise des expressions logiques (enfin, finalement la logique est mathématique).

À mon avis, la principale différence entre la programmation fonctionnelle et la programmation logique réside dans les "éléments constitutifs": la programmation fonctionnelle utilise des fonctions tandis que la programmation logique utilise des prédicats. Un prédicat n'est pas une fonction; il n'a pas de valeur de retour. Selon la valeur de ses arguments, il peut être vrai ou faux; si certaines valeurs ne sont pas définies, il essaiera de trouver les valeurs qui rendraient le prédicat vrai.

Prolog en particulier utilise une forme spéciale de clauses logiques nommées clauses Horn qui appartiennent à la logique du premier ordre; Hilog utilise des clauses de logique d'ordre supérieur.

Lorsque vous écrivez un prédicat de prologue, vous définissez une clause corne: foo :- bar1, bar2, bar3. Signifie que foo est vrai si bar1, bar2 et bar3 est vrai. notez que je n'ai pas dit si et seulement si; vous pouvez avoir plusieurs clauses pour un prédicat:

foo:-
   bar1.
foo:-
  bar2.

signifie que foo est vrai si bar1 est vrai ou si bar2 est vrai

Certains disent que la programmation logique est un surensemble de programmation fonctionnelle puisque chaque fonction pourrait être exprimée comme un prédicat:

foo(x,y) -> x+y.

pourrait s'écrire

foo(X, Y, ReturnValue):-
   ReturnValue is X+Y.

mais je pense que de telles déclarations sont un peu trompeuses

Une autre différence entre la logique et la fonctionnalité est le retour en arrière. En programmation fonctionnelle, une fois que vous entrez dans le corps de la fonction, vous ne pouvez pas échouer et passer à la définition suivante. Par exemple, vous pouvez écrire

abs(x) -> 
   if x>0 x else -x

ou même utiliser des gardes:

abs(x) x>0 -> x;
abs(x) x=<0 -> -x.

mais tu ne peux pas écrire

abs(x) ->
   x>0,
   x;
abs(x) ->
   -x.

d'autre part, dans Prolog, vous pouvez écrire

abs(X, R):-
   X>0,
   R is X.
abs(X, R):-
   R is -X.

si alors vous appelez abs(-3, R), Prolog essaierait la première clause et échouerait lorsque l'exécution atteindrait le point -3 > 0 mais vous n'obtiendrez pas d'erreur; Prolog essaiera la deuxième clause et renverra R = 3.

Je ne pense pas qu'il soit impossible pour un langage fonctionnel d'implémenter quelque chose de similaire (mais je n'ai pas utilisé un tel langage).

Dans l'ensemble, bien que les deux paradigmes soient considérés comme déclaratifs, ils sont très différents; si différents que les comparer ressemble à comparer des styles fonctionnels et impératifs. Je suggère d'essayer un peu de programmation logique; ce devrait être une expérience époustouflante. Cependant, vous devez essayer de comprendre la philosophie et non pas simplement écrire des programmes; Prolog vous permet d'écrire dans un style fonctionnel voire impératif (avec des résultats monstrueux).

60
Thanos Tintinidis

En un mot:

En programmation fonctionnelle, votre programme est un ensemble de définitions de fonctions. La valeur de retour de chaque fonction est évaluée comme une expression mathématique, en utilisant éventuellement des arguments passés et d'autres fonctions définies. Par exemple, vous pouvez définir une fonction factorial, qui retourne une factorielle d'un nombre donné:

factorial 0 = 1                       // a factorial of 0 is 1
factorial n = n * factorial (n - 1)   // a factorial of n is n times factorial of n - 1 

En programmation logique, votre programme est un ensemble de prédicats. Les prédicats sont généralement définis comme des ensembles de clauses, où chaque clause peut être définie à l'aide d'expressions mathématiques, d'autres prédicats définis et du calcul propositionnel. Par exemple, vous pouvez définir un prédicat `` factoriel '', qui est valable chaque fois que le deuxième argument est un factoriel du premier:

factorial(0, 1).               // it is true that a factorial of 0 is 1
factorial(X, Y) :-             // it is true that a factorial of X is Y, when all following are true:
    X1 is X - 1,                   // there is a X1, equal to X - 1,
    factorial(X1, Z),              // and it is true that factorial of X1 is Z, 
    Y is Z * X.                    // and Y is Z * X

Les deux styles permettent d'utiliser des expressions mathématiques dans les programmes.

27
socha23

Premièrement, il existe de nombreux points communs entre la programmation fonctionnelle et la programmation logique. Autrement dit, de nombreuses notions développées dans une communauté peuvent également être utilisées dans l'autre. Les deux paradigmes ont commencé avec des implémentations plutôt grossières et visent la pureté.

Mais vous voulez connaître les différences.

Je prendrai donc Haskell d'un côté et Prolog avec des contraintes de l'autre. Pratiquement tous les systèmes Prolog actuels offrent des contraintes de quelque sorte, comme B, Ciao, Eclipse, GNU, IF, SICStus, SWI, YAP, XSB. Pour les besoins de l'argument, je vais utiliser une contrainte très simple dif/2 signifiant l'inégalité, qui était présente même dans la toute première implémentation de Prolog - donc je n'utiliserai rien de plus avancé que cela.

Quelle programmation fonctionnelle manque

La différence la plus fondamentale tourne autour de la notion de variable. En programmation fonctionnelle, une variable dénote une valeur concrète. Cette valeur ne doit pas être entièrement définie, mais seules les parties définies peuvent être utilisées dans les calculs. Considérez dans Haskell:

> let v = iterate (tail) [1..3] 
> v
[[1,2,3],[2,3],[3],[],*** Exception: Prelude.tail: empty list

Après le 4ème élément, la valeur n'est pas définie. Néanmoins, vous pouvez utiliser les 4 premiers éléments en toute sécurité:

> take 4 v
[[1,2,3],[2,3],[3],[]]

Notez que la syntaxe dans les programmes fonctionnels est astucieusement restreinte pour éviter qu'une variable ne soit laissée indéfinie.

En programmation logique, une variable n'a pas besoin de se référer à une valeur concrète. Donc, si nous voulons une liste de 3 éléments, nous pourrions dire:

?- length(Xs,3).
Xs = [_G323, _G326, _G329].

Dans cette réponse, les éléments de la liste sont des variables. Toutes les instances possibles de ces variables sont des solutions valides. Comme Xs = [1,2,3]. Maintenant, disons que le premier élément doit être différent des autres éléments:

?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys).
Xs = [X, _G639, _G642],
Ys = [_G639, _G642],
dif(X, _G642),
dif(X, _G639).

Plus tard, nous pourrions exiger que les éléments dans Xs soient tous égaux. Avant de l'écrire, je vais l'essayer seul:

?- maplist(=(_),Xs).
Xs = [] ;
Xs = [_G231] ;
Xs = [_G231, _G231] ;
Xs = [_G231, _G231, _G231]  ;
Xs = [_G231, _G231, _G231, _G231] .

Vous voyez que les réponses contiennent toujours la même variable? Maintenant, je peux combiner les deux requêtes:

?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys), maplist(=(_),Xs).
false.

Donc, ce que nous avons montré ici, c'est qu'il n'y a pas de liste de 3 éléments où le premier élément est différent des autres éléments et tous les éléments sont égaux.

Cette généralité a permis de développer plusieurs langages de contraintes qui sont proposés en tant que bibliothèques aux systèmes Prolog, les plus importants sont CLPFD et CHR .

Il n'y a aucun moyen simple d'obtenir des fonctionnalités similaires dans la programmation fonctionnelle. Vous pouvez émuler des choses, mais l'émulation n'est pas tout à fait la même.

Quelle programmation logique manque

Mais il y a beaucoup de choses qui manquent dans la programmation logique qui rendent la programmation fonctionnelle si intéressante. En particulier:

Programmation d'ordre supérieur: La programmation fonctionnelle a ici une très longue tradition et a développé un riche ensemble d'expressions idiomatiques. Pour Prolog, les premières propositions remontent au début des années 80, mais ce n'est pas encore très courant. Au moins ISO Prolog a maintenant l'homologue à appliquer appelé call/2, call/3 ....

Lambdas: Encore une fois, il est possible d'étendre la programmation logique dans cette direction, le système le plus important est Lambda Prolog . Plus récemment, des lambdas ont également été développés pour ISO Prolog .

Systèmes de type: Il y a eu des tentatives, comme Mercure, mais cela n'a pas pris grand-chose. Et il n'y a pas de système avec des fonctionnalités comparables aux classes de types.

Pureté: Haskell est entièrement pur, une fonction Integer -> Integer est une fonction. Pas de petits caractères cachés. Et vous pouvez toujours effectuer des effets secondaires. Des approches comparables évoluent très lentement.

Il existe de nombreux domaines où la programmation fonctionnelle et logique se chevauchent plus ou moins. Par exemple, le retour en arrière et la paresse et la compréhension de listes, l'évaluation paresseuse et freeze/2, when/2, block. DCG et monades. Je laisserai la discussion de ces questions à d'autres ...

22
false

La programmation logique et la programmation fonctionnelle utilisent différentes "métaphores" pour le calcul. Cela affecte souvent la façon dont vous envisagez de produire une solution, et signifie parfois que différents algorithmes arrivent naturellement à un programmeur fonctionnel plutôt qu'à un programmeur logique.

Les deux sont basés sur des fondements mathématiques qui offrent plus d'avantages pour le code "pur"; code qui ne fonctionne pas avec des effets secondaires. Il existe des langages pour les deux paradigmes qui imposent la pureté, ainsi que des langages qui permettent des effets secondaires non contraints, mais culturellement, les programmeurs de ces langages ont tendance à toujours valoriser la pureté.

Je vais considérer append, une opération assez basique en programmation logique et fonctionnelle, pour ajouter une liste à la fin d'une autre liste.

En programmation fonctionnelle, nous pourrions considérer append comme quelque chose comme ceci:

append [] ys = ys
append (x:xs) ys = x : append xs ys

En programmation logique, nous pourrions considérer append comme quelque chose comme ceci:

append([], Ys, Ys).
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).

Ceux-ci implémentent le même algorithme et fonctionnent même essentiellement de la même manière, mais ils "signifient" quelque chose de très différent.

La fonction append définit la liste qui résulte de l'ajout de ys à la fin de xs. Nous pensons à append comme une fonction de deux listes à une autre liste, et le système d'exécution est conçu pour calculer le résultat de la fonction lorsque nous l'invoquons sur deux listes.

La logique append définit une relation entre trois listes, ce qui est vrai si la troisième liste correspond aux éléments de la première liste suivis des éléments de la deuxième liste. Nous pensons à append comme un prédicat qui est vrai ou faux pour 3 listes données, et le système d'exécution est conçu pour trouver des valeurs qui rendront ce prédicat vrai lorsque nous l'invoquerons avec certains arguments liés à des listes spécifiques et certains non liés.

La chose qui rend logique append différent est que vous pouvez l'utiliser pour calculer la liste qui résulte de l'ajout d'une liste à une autre, mais vous pouvez également l'utiliser pour calculer la liste que vous devez ajouter à la fin d'une autre pour obtenir une troisième liste (ou s'il n'existe pas de telle liste), o pour calculer la liste à laquelle vous devez ajoutez-en un autre pour obtenir une troisième liste, o pour vous donner deux listes possibles qui peuvent être ajoutées ensemble pour obtenir un tiers donné (et pour explorer toutes les façons possibles de le faire).

Bien qu'ils soient équivalents en ce que vous pouvez faire tout ce que vous pouvez faire l'un dans l'autre, ils conduisent à différentes façons de penser votre tâche de programmation. Pour implémenter quelque chose dans la programmation fonctionnelle, vous réfléchissez à la façon de produire votre résultat à partir des résultats d'autres appels de fonction (que vous devrez peut-être également implémenter). Pour implémenter quelque chose dans la programmation logique, vous pensez aux relations entre vos arguments (dont certains sont entrés et d'autres qui sont sortis, et pas nécessairement les mêmes d'un appel à l'autre) impliqueront la relation souhaitée.

17
Ben

Prolog, étant un langage logique, vous permet de revenir en arrière gratuitement, c'est assez visible.

Pour élaborer, et je précise que je ne suis aucunement expert dans aucun des paradigmes, il me semble que la programmation logique est bien meilleure quand il s'agit de résoudre des choses. Parce que c'est précisément ce que fait le langage (cela apparaît clairement lorsqu'un retour arrière est nécessaire par exemple).

4
m09

Je pense que la différence est la suivante:

  • programmation impérative = actions de modélisation
  • programmation fonction = raisonnement de modélisation
  • programmation logique = connaissances de modélisation

choisissez ce qui vous convient le mieux

4
Periklis Georgiou

programmation fonctionnelle: à 18h, lumière allumée. programmation logique: lorsqu'il est sombre, allumé.

1
Pigeon The