• Aucun résultat trouvé

Les opérations arithmétiques

Dans le document Apprendre et enseigner Prolog pdf (Page 112-119)

Exemple 3 : Cet exemple montre comment utiliser une variable pour contrôler un programme de l'extérieur La règle liste_de_un qui a été donnée

4. Opérations prédéfinies sur les données

4.2. Les opérations arithmétiques

L'évaluation d'une expression est faite au moyen de la règle prédéfinie val. Le résultat d'une évaluation est soit un entier, soit un réel, soit un identificateur, soit une chaîne. Les booléens sont représentés par les entiers 0 et 1.

val(t1, t2) tval(t1, t2)

Evaluent l'expression t1 et unifient le résultat avec t2. L'expression à évaluer est construite récursivement à partir des constantes, des identificateurs, des éléments de tableau et des fonctions évaluables. Les optimisations (si elles n'ont pas été désactivées au lancement de Prolog) du code généré pour l'appel au prédicat val ne permettent pas, pour la décompilation ou la mise au point, de restituer le terme d'origine mais un terme équivalent. De plus elles suspendent momentanément le mécanisme de gestion automatique des espaces et des piles. Le prédicat tval lui, n'est pas optimisé. On pourra le préférer à val dans les cas de programmes consommateurs d'espace où val manipule fréquemment des termes complexes. On évitera ainsi d'avoir à faire une gestion "manuelle" de la mémoire.

Exemples:

>val(add(mul(2, add(3, 4)), 1000), x); {x=1014}

>val(add(mul(2e0, add(3, 4e0)), 1000), x); {x=1.014000e+03}

>val(2 * (3 + 4) + 1000, x); {x=1014}

Les fonctions arithmétiques peuvent avoir des arguments de types différents, dans ce cas il y a une conversion automatique des types en type le plus général (les types sont, par ordre décroissant: réel, entier). Quand une fonction à évaluer a un nombre incorrect d'arguments ou quand certains de ses arguments sont du mauvais type, val produit une erreur. Cette erreur est récupérable par la règle prédéfinie block.

• La valeur d'un nombre ou d'une chaîne est égale à ce nombre ou à cette chaîne.

• La valeur d'un tableau (externe ou interne) indicé est égale à la valeur de l'élément correspondant de ce tableau. Exemple:

>def_array(tab,100) assign(tab[50],3); {}

>val(tab[50],_x); {_x=3}

• La valeur d'un identificateur i est définie comme suit :

(1) si un terme t a été affecté à i (au moyen de la règle assign : voir le paragraphe suivant) alors la valeur de i est t.

(2) si i n'a pas fait l'objet d'une affectation préalable, alors la valeur de i est i lui-même.

Exemple :

>assign(un, 1); {}

>val(un, x) val(deux, y); {x=1,y=deux}

Certaines fonctions évaluables peuvent aussi être exprimées à l'aide d'opérateurs. Les fonctions évaluables sont :

add(t1, t2) ou t1 + t2

valeur(add(t1, t2)) = valeur(t1) + valeur(t2).

Les valeurs de t1 et t2 doivent être de type entier ou réel. sub(t1, t2) ou t1 - t2

valeur(sub(t1, t2)) = valeur(t1) - valeur(t2).

Les valeurs de t1 et t2 doivent être de type entier ou réel. mul(t1, t2) ou t1 * t2

valeur(mul(t1, t2)) = valeur(t1) * valeur(t2).

Les valeurs de t1 et t2 doivent être de type entier ou réel. div(t1, t2) ou t1 / t2

valeur(div(t1, t2)) = valeur(t1) / valeur(t2).

Les valeurs de t1 et t2 doivent être de type entier ou réel. mod(t1, t2) ou t1 mod t2

valeur(mod(t1, t2)) = valeur(t1) modulo valeur(t2).

mod (modulo) se différencie de rem (remainder) dans le cas où les opérandes t1 et t2 sont de signes contraires. En effet dans ce cas, le second opérande (t2) est ajouté au reste de la division entière de t1 par t2 (remainder).

Les valeurs de t1 et t2 doivent être de type entier. rem(t1, t2) ou t1 rem t2

valeur(rem(t1, t2)) = reste de la division entière de t1 par t2. Les valeurs de t1 et t2 doivent être de type entier.

eql(t1, t2) ou t1 =:= t2 ou =:=(t1, t2)

valeur(eql(t1, t2)) = si valeur(t1) = valeur(t2) alors 1 sinon 0.

Si t1 et t2 sont de type arithmétique, la conversion automatique des types est appliquée avant l'évaluation.

'=\='(t1, t2) ou t1 =\= t2

valeur(=\=(t1, t2)) = si valeur(t1) ! valeur(t2) alors 1 sinon 0.

Si t1 et t2 sont de type arithmétique, la conversion automatique des types est appliquée avant l'évaluation.

inf(t1, t2) ou t1 '<' t2

valeur(inf(t1, t2)) = si valeur(t1) < valeur (t2) alors 1 sinon 0.

Pour les entiers et les réels, on prend la relation "<" entre les nombres. Pour les chaînes, on prend l'ordre alphabétique et pour les identificateurs, on prend

infe(t1, t2) ou t1 '=<' t2

valeur(infe(t1, t2)) = si valeur(t1) " valeur (t2) alors 1 sinon 0. Cf. inf. sup(t1, t2) ou t1 '>' t2

valeur(sup(t1, t2)) = si valeur(t1) > valeur (t2) alors 1 sinon 0. Cf. inf. supe(t1, t2) ou t1 '>=' t2

valeur(supe(t1, t2)) = si valeur(t1) # valeur (t2) alors 1 sinon 0. Cf. inf. if(t, t1, t2)

valeur(if(t, t1, t2)) = si (valeur(t)!0) alors valeur(t1) sinon valeur(t2). sign(t)

valeur (sign(t))= si valeur(t) = 0 alors 0, si valeur(t) < 0 alors -1, sinon 1. La valeur de t doit être de type entier ou réel.

ceiling(t)

valeur (ceiling(t))= - (partie entière(-valeur(t)))

La valeur de t doit être de type entier ou réel. Le résultat est de type entier. floor(t)

valeur (floor(t)) = partie entière(valeur(t)) .

La valeur de t doit être de type entier ou réel. Le résultat est de type entier. round(t)

valeur(round(t))= partie entière(valeur(t)+0.5).

La valeur de t doit être de type entier ou réel. Le résultat est de type entier. trunc(t)

valeur(trunc(t)) = conversion en entier de la valeur de t. La valeur de t doit être de type entier ou réel.

float(t)

valeur(float(t)) = conversion en réel de la valeur de t. La valeur de t doit être de type entier ou réel.

double(t)

valeur(double(t)) = conversion en réel de la valeur de t. La valeur de t doit être de type entier ou réel.

abs(t)

valeur(abs(t)) = valeur absolue de la valeur de t. La valeur de t doit être de type entier ou réel.

Les fonctions suivantes doivent être appliquées à des arguments de type entier. Elles donnent un résultat de type entier.

'/\'(t1,t2)

valeur('/\'(t1,t2)) = 'et' bit à bit entre t1 et t2. '\/'(t1,t2)

valeur('\/'(t1,t2)) = 'ou' bit à bit entre t1 et t2. '<<'(t1,t2)

valeur('<<'(t1,t2)) = t1 décalé de t2 bits vers la gauche. '>>'(t1,t2)

valeur('>>'(t1,t2)) = t1 décalé de t2 bits vers la droite. '~'(t)

valeur('~'(t)) = complément bit à bit de t.

Les fonctions suivantes doivent être appliquées à des entiers ou des réels et donnent un résultat de type réel. Les fonctions trigonométriques travaillent avec des angles exprimés en radians.

atan(t)

valeur (atan(t)) = arc tangente(valeur(t)). cos(t)

valeur (cos(t)) = cosinus(valeur(t)). exp(t)

valeur (exp(t)) = exponentielle(valeur(t)). ln(t)

valeur(ln(t)) = logarithme népérien(valeur(t)). rad(t)

valeur(rad(t)) = conversion en radian(valeur(t)). sin(t)

valeur(sin(t)) = sinus(valeur(t)). sqrt(t)

valeur(sqrt(t)) = racine carrée(valeur(t)). tan(t)

valeur(tan(t)) = tangente(valeur(t)). t1 ** t2

4.3. Affectation

assign(i, t) tassign(i, t) cassign(i, t)

Affectent le terme t à l'identificateur i, t peut être un terme Prolog quelconque. Tout se passe comme si i devenait le nom d'une variable «globale» (ultérieurement accessible pendant l'effacement de n'importe quel but) et «statique» (résistante au backtracking) possédant t pour valeur. Il s'agit donc bien de l'affectation classique, comme elle se pratique dans FORTRAN, Pascal,

etc…. Les optimisations (si elles n'ont pas été désactivées au lancement de Prolog) du code généré pour l'appel au prédicat assign suspendent momentanément le mécanisme de gestion automatique des espaces et des piles. Le prédicat tassign lui, n'est pas optimisé. On pourra le préférer à assign dans les cas de programmes consommateurs d'espace où assign manipule fréquemment des termes complexes. On évitera ainsi d'avoir à faire une gestion "manuelle" de la mémoire. Le prédicat cassign est similaire au prédicat tassign mais permet de conserver les contraintes (dif, freeze) et les attributs (variables attribuées) associés aux variables du terme t.

Exemple : >assign(nom_fichier, "monfichier.txt"); {} >val(nom_fichier,x) {x="monfichier.txt"} >

En Prolog, ces variables statiques peuvent être vues comme une manière particulièrement efficace de réaliser des assertions (ou règles sans queue). Du point de vue de l'utilisateur, on peut considérer que l'emploi de assign et val fait ci-dessus est conceptuellement équivalent à:

>retract(nom_fichier(x), nil); >assert(nom_fichier("monfichier.txt"), nil); {} >nom_fichier(x); {x="monfichier.txt"} >

Exemple : possibilité d'affecter un terme quelconque, et non uniquement des constantes.

>assign(myterm, jean.<34,"rue blanche">.A_utres_infos); {} >val(myterm,x) val(myterm,y); {x=jean.<34,"rue blanche">.v64,y=jean.<34,"rue blanche">.v65} > assign(tab(i), t) ou assign(tab[i], t) tassign(tab(i), t) ou tassign(tab[i], t)

Pour l'accès à des éléments de tableaux (dans val) les notations tab(i) et tab[i] sont équivalentes; cependant, elles ne sont pas représentées par les mêmes termes Prolog, et dans le cas de l'utilisation de tab[i] le compilateur optimise l'accès au tableau. Pratiquement, on réservera la notation tab[i] pour la désignation d'éléments de tableau uniquement. Nous ferons également la même remarque que précédemment concernant les optimisations : assign est optimisé, tassign ne l'est pas.

backtrack_term

Tableau prédéfini du module sys, de longueur 100 (indices possibles entre 1 et 100) qui peut être manipulé à l'aide des prédicats prédéfinis val ou tval et assign ou tassign. Les valeurs qui lui sont assignées sont perdues au backtracking. Chaque élément du tableau est initialisé à 0. Exemple:

> insert; test(i) -> assign(backtrack_term[1],111) val(backtrack_term[1],i); test(i) -> val(backtrack_term[1],i); ; {} > test(i); {i=111} {i=0} def_array(i, n)

Définit dynamiquement un tableau de termes Prolog de nom i et de taille n. Ce tableau se comportera comme une variable «globale» (ultérieurement accessible pendant l'effacement de n'importe quel but) et «statique» (résistante au backtracking). Chaque élément du tableau est initialisé avec l'entier 0. Les valeurs légales de l'indice sont incluses dans 1..n.

Si un tableau de même nom existe déjà :

- s'il s'agit d'un tableau de même taille, il ne se passe rien - si les tailles diffèrent, il se produit une erreur

L'accès et l'affectation sont analogues à ceux des tableaux des autres langages de programmation : voir ci-dessus val pour l'accès et assign pour l'affectation. Le tableau est désalloué lorsqu'on tue le module auquel il appartient (c.à.d. les règles ayant le même préfixe) ou bien lorsqu'on exécute le prédicat kill_array.

"gestion d'une pile"

inc(i) -> val(i + 1, x) assign(i, x); dec(i) -> val(i - 1, x) assign(i, x);

initialise -> assign(pointeur,0) def_array(pile,100); empile(v) ->

inc(pointeur)

val(inf(pointeur,100),1) !

depile(v) ->

val(eql(pointeur,0),0) !

val(pile[pointeur],v) dec(pointeur);

depile(v) -> outm("pile vide") line fail;;

>initialise; {} >empile(12345); {} >empile(23456); {} >depile(x) depile(y); {x=23456, y=12345} >depile(x); redef_array(i, n)

Analogue à def_array mais, si le tableau de nom i existe déjà, redéfinit sa taille: n, conserve les affectations existantes pour les indices valides, et initialise s'il y a lieu les nouveaux éléments.

is_array(i,t)

Vérifie que l'identificateur i désigne bien un tableau et unifie t avec sa taille. kill_array(i)

Détruit le tableau de nom i. Si le tableau n'est pas défini, le prédicat s'efface en imprimant éventuellement1 un warning.

Dans le document Apprendre et enseigner Prolog pdf (Page 112-119)