La correspondance que l'on vient d'évoquer n'est valide que si
la logique de Prolog comprend l'axiome de
-équivalence*.
Autrement,
deux termes non-
-équivalents,
et
,
ne seraient pas distingués car
.
Muni de la -équivalence,
on peut pousser cette correspondance jusqu'à un idiome de programmation
où
-abstraction et quantification universelle sont symétriques.
Cela permet de convertir à volonté des
-abstractions en des
quantifications universelles et des
-variables en des constantes
universelles.
Cette symétrie sert de fil directeur à notre
reconstruction pragmatique de
Prolog* [Belleannée et al. 95],
mais elle a aussi des conséquences importantes pour
l'implémentation
[Brisset et Ridoux 92b].
Figure 7: Réduction de graphe et -rédex
La représentation des termes qui est la plus commode pour l´unification*
modulo -équivalence est leur
forme normale de tête
-longue* [Huet 75].
Pour ne pas avoir à la calculer à chaque unification,
les termes sont systématiquement représentés de cette manière dès leur
création.
On construit ainsi beaucoup de
-abstractions que l'on peut juger
artificielles,
et aussi beaucoup de
-rédex qui le sont autant.
Il ne faudrait pas mobiliser toute la procédure de réduction de graphe pour ces
-rédex.
Pour cela,
on distingue les
-abstractions issues d'une
-expansion
en les appelant des
-abstractions.
La procédure de réduction de graphe peut alors reconnaître les
-rédex
construits à l'aide de
-abstractions :
on les appelle des
-rédex*.
Ceux là peuvent être réduits en construisant simplement une nouvelle
application
(voir à la figure 7
la nouvelle application
où le pointeur vers
est remplacé par un pointeur vers
).
Il ne faut noter comme tels que les
-rédex qui le
resteront quelles que soient les
-réductions qui leur seront appliquées :
ce sont les
-rédex qui sont en
forme normale de tête*.
Les -variables sont aussi
-expansées et cela est la cause
d'une autre forme de
-rédex «abusifs».
À cause de la
-expansion,
une
-variable
de type fonctionnel aura toutes ces occurrences
remplacées par
.
Lorsqu'une
-réduction cause le remplacement de la
-variable
par un terme
,
c'est toute sa forme
-expansée qu'il faut remplacer,
et pas seulement
.
En effet,
remplacer seulement
par
créerait des
-rédex parasites de la forme
.
Il faut donc reconnaître les
-variables
-expansées.
Cela se fait de la même manière que pour reconnaître les
-rédex.
La combinaison de ces deux heuristiques produit un gain de complexité
important.
Par exemple,
sachant que dénote
et est
-expansé en
et que
dénote
,
le terme
est
-expansé en
,
et se
-normalise naïvement en un nombre d'étapes
qui est en cube du nombre de
.
Ce nombre devient quadratique quand la première heuristique est appliquée
et linéaire lorsque les deux le sont.
On peut s'en convaincre en regardant la trace de la
-réduction
naïve de la forme
-expansée de
.
Le coût de duplication est proportionnel à la taille des termes soulignés.
Le coût total est donc cubique.
Avec la première heuristique,
les étapes de -réduction sont les mêmes,
mais il n'y a plus de duplication.
Le coût total devient quadratique.
La deuxième heuristique va éviter d'emboîter des
-abstractions.
Il y a donc moins de
-rédex et moins de
-réductions.
La trace de la
-réduction avec les deux heuristiques est la suivante,
où les numéros correspondent aux mêmes étapes que pour la
-réduction
naïve.
Le coût de la -réduction de chaque
est constant.
Le coût total est donc linéaire avec le nombre de
.
Le point de départ de cette discussion sur l'effet de la
-équivalence
sur l'implémentation était la symétrie entre les deux quantifications
essentiellement universelles,
et
.
Cette symétrie se traduit par des remplacements de
-variables
par des constantes universelles*,
ou de constantes universelles par des
-variables.
Ces remplacements sont fréquents et le programmeur les exprime par des
-rédex.
Il est aussi fréquent qu'un remplacement soit suivi peu après par le
remplacement inverse.
À cause des duplications qu'elle implique
(même en considérant les heuristiques décrites),
la réduction de graphe n'est pas le bon choix pour implémenter ce qui n'est
en fait qu'un changement de point de vue sur un terme.
Nous avons donc choisi d'adapter la technique des
substitutions explicites* [Revesz 88]
à la réduction de graphe,
mais uniquement pour traiter la correspondance entre
constantes universelles et
-variables
(
*).
Cela permet de défaire l'effet d'un remplacement simplement en enlevant
la substitution explicite qui le représente.
Il faut bien sûr trouver une représentation
de ces nouveaux objets,
qui sont essentiellement des triplets de termes
(pour
remplace
dans
),
et programmer les opérations associées :
création, propagation, suppression et réalisation de substitution explicite.
De ces deux développements un peu techniques sur le rôle des combinateurs
et de la -équivalence,
il faut retenir qu'un système complexe ne se
range pas facilement dans une catégorie préétablie.
Par exemple,
notre implémentation de
Prolog met en
uvre la réduction de graphe,
mais lui ajoute des améliorations qui correspondent à des traits particulier
de
Prolog.
La réduction de graphe fournit un cadre général dont la principale qualité
est la simplicité,
celle de la gestion de mémoire en particulier.
Nous l'avons choisi pour cela.
La réduction de graphe prévoit des opérations qui sont parfois coûteuses,
et une première forme d'amélioration consiste à trouver des heuristiques
qui permettent de remplacer les opérations coûteuses par des opérations
qui le sont moins ou même de simplement les éviter.
Une seconde forme d'amélioration consiste à ajouter à la réduction de graphe
des opérations qui viennent d'autres paradigmes comme les substitutions
explicites.