• Aucun résultat trouvé

Nous proposons ici un certain nombre de programmes, concernant plus particulièrement les tuples, qui montrent des possibilités d'utilisation. Certains utilisant des contraintes retardées, il serait bon de se reporter au chapitre correspondant si l'on désire les examiner dans le détail.

Calcul des feuilles d’un arbre

Le programme suivant recherche les feuilles différentes de <> dans un arbre. On notera la puissance de l’opérateur général de construction d’arbres, ainsi que l’utilisation des contraintes retardées.

feuilles(<>,<>) ->;

feuilles(A[<>], <A>) ->, {A#<>}; feuilles(A[<U>.V], X.Y) ->

feuilles (U, X) feuilles (V, Y);

Voici une exécution de ce programme :

> feuilles(Erable("acer",feuilles(lobes(7),

caduques(1'))), L);

{L = "acer".<7,1'>}

Quick sort

Voici un programme de tri récursif, le “quick sort”, qui utilise les contraintes sur les tuples. Rappelons brièvement l’algorithme utilisé. Pour trier un tuple composé de nombres, on choisit un élément, on partitionne le tuple en deux, le premier tuple contenant les éléments inférieurs ou égaux à l’élément considéré, le second les éléments strictement supérieurs. On applique alors récursivement l’algorithme sur les deux tuples ainsi formés jusqu’à arriver au tuple vide.

Le choix de l’élément privilégié peut s’effectuer de diverses manières. Nous avons choisi ici, afin de mettre en évidence l’accès direct, de choisir l’élément situé au milieu du tuple.

trier(<>,<>) -> ; trier(L,U.<X>.V) -> partitionner(L,X,U',V') trier(U',U) trier(V',V); partitionner(L,X,U',V') -> trunc(N/2,N') regrouper(X,L',U',V'), { L :: N, U :: N', L = U.<X>.V, L' = U.V}; regrouper(X,<>,<>,<>) ->; regrouper(X,<X'>.L,<X'>.U,V) -> regrouper(X,L,U,V), { X' <= X}; regrouper(X,<X'>.L,U,<X'>.V) -> regrouper(X,L,U,V), { X' > X};

On peut proposer un certain nombre d'exécutions de ce programme. la première consiste à effectuer un tri sur un tuple entièrement connu :

> trier(<6,5,4,3,2,1>,L>); {L = <1,2,3,4,5,6>}

On peut également chercher les tuples qui, une fois triés, donnent un tuple connu. Par exemple :

{L = <1,2,3>} {L = <1,3,2>} {L = <2,1,3>} {L = <2,3,1>} {L = <3,2,1>} {L = <3,1,2>} >

Cette exécution fournit, bien sûr, toutes les permutations de n éléments. Enfin, on peut trier un tuple dont les éléments sont inconnus, mais sur lesquels on connait déjà un ordre :

> trier(<X,Y,Z>,L),{Z>Y>=X}; {Y = X + X1, Z = X + X2 + X1, L = <X,X + X1,X + X2 + X1>,

X1 >= 0 , X2 > 0 }

Les variables d'écart introduites ont été renommées X1 et X2.

Une suite périodique

Le but de ce programme est de montrer que la suite définie par : Xi+2 = |Xi+1| - Xi

est périodique de période 9 Voici le programme :

Suite(<Y,X>) ->;

Suite(<Y'-X,Y,X>.U) -> Suite(<Y,X>.U)

Valeur_absolue(Y,Y) -> , { Y >= 0 }; Valeur_absolue(Y,-Y) -> , { Y < 0 };

Ce programme se contente de décrire la construction d'un tuple dont les éléments constituent une suite d'éléments de la suite définie précédemment. Pour prouver que cette suite est périodique de période 9, toute la subtilité consiste à poser une requête qui peut s'énoncer de la manière suivante : Quels sont les tuples de 11 éléments décrits par ce programme qui sont tels que l'un ou l'autre des deux premiers éléments soient différents de l'un ou l'autre des deux derniers éléments. Voici cette requête :

> Suite(U.V.W) ,

{ U :: 2, V :: 7, W :: 2, U # W };

>

La requête échoue, ce qui signifie que l'ensemble des solutions est vide, et prouve donc bien la propriété (!!). Elégant, non ?

Crible d'Eratosthène

Ce programme, qui calcule les nombres premiers inférieurs à un entier N, peut s'écrire de manière plus efficace sans utiliser l'arithmétique sur la taille des tuples. Il constitue cependant un excellent exemple de maniement des tuples et de la concaténation. Voici le programme :

cribles(2, X, Z), {Z :: N, X :: N, X.<1'> = <1'>.X}; cribles(N, <>, <>) ->; cribles(N, <0'>.X, <0'>.Z) -> cribles(N+1, X, Z); cribles(N, <1'>.X, <1'>.Z) -> crible(N, X, Y) cribles(N+1, Y, Z); crible(N, X, X) -> {X :: M, N > M}; crible(N, X, Y) -> Crible(N, X', Y') { X :: M, N <= M, X = U.<E>.X', Y = U.<0'>.Y', U :: N-1 };

Pour construire le tuple formé de valeurs booléennes où le rang de tout élément égal à 1' est un nombre premier, on procède de la manière suivante (prédicat cribles) : pour un élément donné, et un nombre donné N (en commençant à 2, et en initialisant le tuple de manière à ce que tous les éléments soient égaux à 1'), si sa valeur est 0', on la renvoie, sinon on renvoie 1', et on unifie à 0' tous les éléments multiples de N par simple décalage (i.e. à l'aide de la concaténation). Cette dernière opération est réalisée par le prédicat crible.

Voici une exécution :

> nombres_premiers(Y), {Y :: 12};

{Y = <1',1',1',0',1',0',1',0',0',0',1',0'>}

Les contraintes numériques

1. Introduction 2. Généralités - Les nombres - Expressions numériques - Les relations - Contraintes numériques

- Exemples de contraintes numériques - Restrictions

- Forme normale

3. Règles prédéfinies et procédures externes spécifiques 4. Retardement des contraintes non-linéaires

5. Formats d'entrée-sortie 6. Exemples de programmes

- Calcul bancaire - Crypto-arihmétique

- Remplissage d’un rectangle par des carrés

Que trouve-t-on dans ce chapitre ?

Parmi les différents domaines sur lesquels on peut faire porter des contraintes en Prolog III, celui pour lequel on pressent d'ores et déjà le plus d'applications est certainement le domaine numérique. En effet, la possibilité, offerte en Prolog III, de résoudre des systèmes d'équations et d'inéquations sur les nombres rationnels et réels ouvre la voie de la programmation logique à tous les problèmes de type ordonnancement, planification, réseaux, analyse financière, etc. Nous aurons l'occasion dans ce chapitre de développer des exemples de ce type, non sans avoir au préalable exposé les diverses possibilités offertes par le langage et concernant ce type de contraintes, de la description syntaxique des systèmes numériques jusqu'à l'utilisation de certains prédicats évaluables spécifiques.

1 . Introduction

Prolog a toujours été considéré, à juste titre, comme un puissant langage symbolique, impropre à tout traitement numérique. Ceci est lié en grande partie à des considérations d'ordre historique, et bons nombres d'interpréteurs et de compilateurs du langage se sont jusqu'à présent bornés à ajouter des couches coiffant l'unification, et permettant de manipuler certaines expressions numériques. Ces traitements consistent en fait à évaluer de telles expression en effectuant des calculs sur des variables dont les valeurs sont connues au moment de l'unification. Prolog III propose une approche qui va bien au-delà de ce simple «sucre syntaxique», puisque les variables représentant des nombres sont traitées comme toutes les variables Prolog III, c'est à dire que ce sont des objets dont la valeur peut parfaitement être inconnue. Ainsi, les expressions numériques ne sont généralement pas évaluées (les valeurs de suffisamment de variables n'étant pas connues) mais forment les principaux composants des contraintes numériques traitées par le langage au niveau de l'unification. Ce traitement consiste principalement à vérifier si l'ajout de ces contraintes ne rend pas le système insoluble ainsi qu'à effectuer un certain nombre d'autres traitements sur lesquels nous reviendrons par la suite.