Recherche…


introduction

De nombreux systèmes Prolog modernes sont en développement continu et ont ajouté de nouvelles fonctionnalités pour résoudre les problèmes classiques du langage. Malheureusement, de nombreux manuels Prolog et même des cours d’enseignement ne présentent toujours que le prologue obsolète. Ce sujet est destiné à illustrer comment Prolog moderne a surmonté certains des problèmes et syntaxe plutôt inutile qui apparaît dans les anciens Prolog et peut encore être introduite.

CLP (FD) pour l'arithmétique des nombres entiers

Traditionnellement, Prolog exécute l'arithmétique à l'aide des opérateurs is et =:= . Cependant, plusieurs Prologs actuels offrent CLP (FD) (Programmation par Logique de Contrainte sur Domaines Finis) comme alternative plus propre à l'arithmétique entière. CLP (FD) est basé sur le stockage des contraintes qui s'appliquent à une valeur entière et à la combinaison de celles-ci en mémoire.

CLP (FD) est une extension dans la plupart des Prolog qui la supporte, elle doit donc être chargée explicitement. Une fois qu'il est chargé, le #= syntaxe peut prendre la place des deux is et =:= . Par exemple, dans SWI-Prolog:

?- X is 2+2.
X = 4.

?- use_module(library(clpfd)).
?- X #= 2+2.
X = 4.

A la différence is , #= est en mesure de résoudre des équations simples et unifier dans les deux sens:

?- 4 is 2+X.
ERROR: is/2: Arguments are not sufficiently instantiated

?- 4 #= 2+X.
X = 2.

CLP (FD) fournit sa propre syntaxe de générateur.

?- between(1,100,X).
X = 1;
X = 2;
X = 3...

?- X in 1..100.
X in 1..100.

Notez que le générateur ne s'exécute pas réellement: seule la contrainte d'intervalle est stockée, prête à être combinée avec des contraintes ultérieures. Le générateur peut être forcé à exécuter (et à forcer des contraintes) en utilisant le prédicat d' label :

?- X in 1..100, label([X]).
X = 1;
X = 2;
X = 3..

L'utilisation de CLP peut permettre une réduction intelligente des cas de force brute. Par exemple, en utilisant l'arithmétique de type entier ancien:

?- trace.
?- between(1,10,X), Y is X+5, Y>10.
...
Exit: (8) 6 is 1+5 ? creep
Call: (8) 6 > 10 ? creep
...
X = 6, Y = 11; ...

Prolog continue à parcourir les valeurs 1-5 même s'il est mathématiquement prouvable que les valeurs données ne permettent pas de les utiliser. En utilisant CLP (FD):

?- X in 1..10, Y #= X+5, Y #> 10.
X is 6..10,
X+5 #= Y,
Y is 11..15.

CLP (FD) effectue immédiatement les calculs et calcule les plages disponibles. L'ajout de label([Y]) entraînera une boucle de X uniquement à travers les valeurs utiles 6..10. Dans cet exemple de jouet, cela n'augmente pas les performances car, avec une plage aussi petite que 1-10, le traitement d'algèbre prend autant de temps que la boucle l'aurait fait; mais lorsque des nombres plus importants sont traités, cela peut réduire le temps de calcul.

La prise en charge de CLP (FD) est variable entre Prologs. Le meilleur développement reconnu de CLP (FD) se trouve dans SICStus Prolog, qui est commercial et coûteux. SWI-Prolog et d'autres prologs ouverts ont souvent une implémentation. Visual Prolog n'inclut pas CLP (FD) dans sa bibliothèque standard, bien que des bibliothèques d'extension soient disponibles.

Forall au lieu de boucles pilotées par les pannes

Certains manuels "classiques" de Prolog utilisent toujours la syntaxe de boucle, déroutante et sujette à erreur, où une construction par fail est utilisée pour forcer le retour en arrière d'appliquer un objectif à chaque valeur d'un générateur. Par exemple, pour imprimer tous les nombres jusqu'à une limite donnée:

fdl(X) :- between(1,X,Y), print(Y), fail.
fdl(_).

La grande majorité des prologues modernes n'ont plus besoin de cette syntaxe, fournissant plutôt un prédicat d'ordre supérieur pour y remédier.

nicer(X) :- forall(between(1,X,Y), print(Y)).

Non seulement cela est beaucoup plus facile à lire, mais si un objectif susceptible d'échouer était utilisé à la place de print , son échec serait correctement détecté et transmis - alors que les échecs des objectifs dans une boucle pilotée par une défaillance sont confondus avec l'échec forcé. qui dirige la boucle.

Visual Prolog a un sucre syntaxique personnalisé pour ces boucles, associé à des prédicats de fonction (voir ci-dessous):

vploop(X) :- foreach Y = std::fromTo(1,X) do
                 console::write(X)
             end foreach.

Bien que cela ressemble à un impératif pour la boucle, il suit toujours les règles Prolog: en particulier, chaque itération de foreach a sa propre portée.

Prédicats de fonction

Traditionnellement, dans Prolog, les "fonctions" (avec une sortie et des entrées liées) étaient écrites en tant que prédicats réguliers:

mangle(X,Y) :- Y is (X*5)+2.

Cela peut créer la difficulté que si un prédicat de style fonction est appelé plusieurs fois, il est nécessaire de "chaîner" les variables temporaires.

multimangle(X,Y) :- mangle(X,A), mangle(A,B), mangle(B,Y).

Dans la plupart des Prologs, il est possible d'éviter cela en écrivant un autre opérateur d'infixe à utiliser à la place de is qui développe des expressions, y compris la fonction alternative.

% Define the new infix operator
:- op(900, xfy, <-).

% Define our function in terms of the infix operator - note the cut to avoid
% the choice falling through
R <- mangle(X) :- R is (X*5)+2, !.

% To make the new operator compatible with is..
R <- X :-
    compound(X),            % If the input is a compound/function
    X =.. [OP, X2, X3],     % Deconstruct it
    R2 <- X2,               % Recurse to evaluate the arguments
    R3 <- X3,
    Expr =.. [OP, R2, R3],  % Rebuild a compound with the evaluated arguments
    R is Expr,              % And send it to is
    !.
R <- X :- R is X, !.        % If it's not a compound, just use is directly

Nous pouvons maintenant écrire:

multimangle(X,Y) :- X <- mangle(mangle(mangle(Y))).

Cependant, certains Prolog modernes vont plus loin et proposent une syntaxe personnalisée pour ce type de prédicat. Par exemple, dans Visual Prolog:

mangle(X) = Y :- Y = ((X*5)+2).
multimangle(X,Y) :- Y = mangle(mangle(mangle(X))).

Notez que l'opérateur <- et le prédicat de style fonctionnel ci-dessus se comportent toujours comme des relations - il est légal d'avoir des points de choix et d'effectuer une unification multiple. Dans le premier exemple, nous évitons cela en utilisant des coupes. Dans Visual Prolog, il est normal d'utiliser la syntaxe fonctionnelle pour les relations et les points de choix sont créés de manière normale - par exemple, l'objectif X = (std::fromTo(1,10))*10 réussit avec les liaisons X = 10 , X = 20, X = 30, X = 40, etc.

Déclarations de flux / mode

Lors de la programmation dans Prolog, il n'est pas toujours possible, ou souhaitable, de créer des prédicats unifiant pour chaque combinaison possible de paramètres. Par exemple, le prédicat between(X,Y,Z) qui exprime que Z est numériquement compris entre X et Y. Il est facilement implémenté dans les cas où X, Y et Z sont tous liés (soit Z est compris entre X et Y, soit ce n'est pas) ou où X et Y sont liés et Z est libre (Z unifie avec tous les nombres entre X et Y ou le prédicat échoue si Y <X); mais dans d'autres cas, tels que X et Z sont liés et Y est libre, il existe potentiellement un nombre infini d'unifications. Bien que cela puisse être mis en œuvre, ce ne serait généralement pas le cas.

Les déclarations de mode ou les déclarations de mode permettent une description explicite du comportement des prédicats lorsqu'ils sont appelés avec différentes combinaisons de paramètres liés. Entre les between , la déclaration serait:

%! between(+X,+Y,+Z) is semidet.
%! between(+X,+Y,-Z) is nondet. 

Chaque ligne spécifie un motif d'appel potentiel pour le prédicat. Chaque argument est décoré avec + pour indiquer les cas où il est lié, ou - pour indiquer les cas où il ne l'est pas (il existe également d'autres décorations pour les types plus complexes tels que les tuples ou les listes pouvant être partiellement liées). Le mot clé after is indique le comportement du prédicat dans ce cas et peut être l'un de ceux-ci:

  • det si le prédicat réussit toujours sans point de choix. Par exemple, add(+X,+Y,-Z) est det car l'ajout de deux nombres donnés X et Y aura toujours exactement une réponse.
  • semidet si le prédicat réussit ou échoue, sans point de choix. Comme ci-dessus, between(+X,+Y,+Z) est semidet parce que Z est soit entre X et Y ou ce n'est pas le cas.
  • multi si le prédicat réussit toujours, mais peut avoir des points de choix (mais aussi peut-être pas). Par exemple, factor(+X,-Y) serait multi car un nombre a toujours au moins un facteur - lui-même - mais peut avoir plus.
  • nondet si le prédicat peut réussir avec des points de choix ou échouer. Par exemple, between(+X,+Y,-Z) est nondet car il peut y avoir plusieurs unifications possibles de Z à des nombres entre X et Y, ou si Y <X, il n'y a pas de nombres entre eux et le prédicat échoue.

Les déclarations de flux / mode peuvent également être combinées avec l’étiquetage des arguments pour clarifier quels termes signifient ou en tapant. Par exemple, between(+From:Int, +To:Int, +Mid:Int) is semidet .

Dans les Prologs purs, les déclarations de flux et de mode sont facultatives et ne sont utilisées que pour la génération de documentation, mais elles peuvent être extrêmement utiles pour aider les programmeurs à identifier la cause des erreurs d'instanciation.

Dans Mercury, les déclarations de flux et de mode (et les types) sont obligatoires et validées par le compilateur. La syntaxe utilisée est comme ci-dessus.

Dans Visual Prolog, les déclarations et les types de flux et de mode sont également obligatoires et la syntaxe est différente. La déclaration ci-dessus serait écrite comme suit:

between : (int From, int To, int Mid) determ (i,i,i) nondeterm (i,i,o).

La signification est la même que ci-dessus, mais avec les différences qui:

  • Les déclarations de flux / mode sont séparées des déclarations de type (car on suppose que le flux / mode pour un seul prédicat ne varie pas avec la surcharge de type);
  • i et o sont utilisés pour + et - et sont associés aux paramètres basés sur la commande;
  • Les termes utilisés sont différents. det devient procedure , semidet devient determ , et nondet devient nondeterm ( multi est toujours multi ).


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow