2010-10-18 24 views
3

Ok, donc j'ai ceGNU Prolog - numéro Recursion (? Facile)

edu_less(hs,college). 
edu_less(college,masters). 
edu_less(masters,phd). 

J'ai besoin d'écrire une fonction pour dire si quelque chose est inférieure à l'autre. Le prédicat est

edu_le. 

Donc, si je mets edu_le(hs,phd). il devrait retourner oui. Je suis venu avec ça. Je ne veux vraiment pas que tout passe par tout et renvoie des réponses multiples.

Est-il possible de renvoyer seulement oui ou non s'il trouve qu'il est en fait inférieur ou égal au 2ème argument? Donc, fondamentalement, si j'utilise à nouveau l'exemple edu_le(hs,phd), alors parce que hs est moins que college, et college est que master, et master est moins que phd, alors hs doit être inférieur à phd et il dirait yes.

Désolé, vraiment nouveau pour prolog, essayant toujours d'obtenir le coup de cette.

Répondre

3

La façon la plus pratique d'écrire de tels prédicats est d'utiliser la découpe (!). La coupe entraîne d'autres clauses à ne pas prendre en compte lors du retour en arrière. Vous écrivez votre prédicat comme suit:

edu_le(A,B) :- A = B, !. 
edu_le(A,B) :- edu_less(A,B), !. 
edu_le(A,B) :- edu_less(A,C), edu_le(C,B). 

La dernière clause n'a pas besoin d'une coupe, car il n'y a pas d'autres clauses à considérer de toute façon. La coupe est placée après tous les tests pour déterminer si la clause doit réussir.

Les puristes de la programmation logique désapprouvent la coupe, car cela rend la signification d'un prédicat dépendante de l'ordre des clauses, ce qui est différent de la logique en mathématiques. !

+0

AHHHHHHH (comme une sainte ahhh). Je vous remercie. Dieu merci, je ne suis pas un puriste, n'est-ce pas? – Matt

2

/0 fait également ce programme incomplet, pensez par exemple la requête la plus générale avec les deux versions:

?- edu_le(X, Y). 

Il est souvent préférable d'utiliser une fois/1 si vous voulez seulement une seule preuve d'un particulier objectif:

?- once(edu_le(hs, phd)). 
3

Dans la définition sous-jacente

edu_le(A,B) :- A = B. 
edu_le(A,B) :- edu_less(A,B). 
edu_le(A,B) :- edu_less(A,C), edu_le(C,B). 

la deuxième claus e est superflu et provoque une génération répétée de réponses. Utilisez

edu_le(A,B) :- A = B. 
edu_le(A,B) :- edu_less(A,C), edu_le(C,B). 

Cela vous donne une réponse true, alors pas plus de réponses (false) sur retours en arrière. Vous pouvez utiliser une coupure dans la première clause, mais la génération ne fonctionnera plus.

?- edu_le(hs,X). 
X = hs ; 
X = college ; 
X = masters ; 
X = phd ; 
false. 

devient incomplète:

?- edu_le(hs,X). 
X = hs. 

Comme tapis suggéré, utiliser once/1 à la place. Dans une bonne implémentation de Prolog, ce prédicat fonctionne comme si votre programme avait des coupures dans des endroits stratégiques, accélérant votre programme sans perturber sa sémantique logique.

1

Je vous suggère de ne pas suivre le chemin proposé par Juho Östman et garder la pureté - sinon, pourquoi devriez-vous utiliser Prolog en première instance? Si vous êtes trop indulgent pour vous en tenir au paradigme logique, vous obtenez des résultats déplaisants. Dans ce cas, le prédicat de Juho est définitivement différent du vôtre, et je vais vous montrer pourquoi. Tout d'abord, il suffit de supprimer la règle inutile edu_le(A,B) :- edu_less(A,B)., comme le suggère larsmans. Vous obtiendrez une version moins redondante de votre prédicat d'origine:

edu_le1(A, A). 
edu_le1(A, B) :- edu_less(A, C), edu_le1(C, B). 

Il se comporte comme edu_le, ce qui signifie: étant donné une requête arbitraire, elle produit exactement la même réponse, à l'exception des doublons (edu_le1 a moins). Vous pouvez juste être heureux avec lui, mais il a encore des réponses redondantes que vous n'aimez peut-être pas; par exemple, sous SWI:

?- edu_le1(hs, hs) 
true ; 
false. 

Maintenant, vous pouvez dire que vous ne l'aimez pas, car il a toujours le false redondant, mais si vous utilisez le prédicat au lieu de Juho (sans la règle inutile):

edu_le2(A, A) :- !. 
edu_le2(A, B) :- edu_less(A, C), edu_le2(C, B). 

il est vrai que vous éliminez l'inutile finale false:

?- edu_le2(hs, hs) 
true. 

?- 

mais vous perdez plus que cela: vous perdez, comme le remarque mat, la possibilité de générer tous les e e solutions quand une variable ne sont pas instanciés:

?- edu_le1(hs, B) %same, with more copies, for edu_le 
B = hs ; 
B = college ; 
B = masters ; 
B = phd ; 
false. 

?- edu_le2(hs, B) 
B = hs.   %bad! 

?- 

En d'autres termes, ce dernier prédicat ne correspond pas à l'ancienne: edu_le et edu_le1 avoir le type edu_le(?A, ?B), alors au lieu edu_le2 a le type edu_le2(+A, +B) (voir [1] pour la sens). Soyez sûr que: edu_le2 est moins utile car il fait moins de choses, et peut donc être réutilisé dans moins de contextes. Ceci parce que la coupe en edu_le2 est une coupe en rouge, c'est-à-dire une coupe qui change la signification du prédicat où elle est introduite. Vous pouvez néanmoins vous en contenter, étant donné que vous comprenez la différence entre les deux. Tout dépend de ce que vous voulez faire avec.

Si vous voulez obtenir le meilleur des deux mondes, vous devez introduire dans edu_le1 une coupe verte appropriée qui réduit la redondance lorsque A et B sont complètement instanciés. Dans ce but, vous devez vérifier que A et B sont instanciés au même terme avant de couper. Vous ne pouvez pas le faire avec =, car = ne vérifie pas, mais unifie. L'opérateur droit est ==:

edu_le3(A, B) :- (A == B -> ! ; true), A = B. 
edu_le3(A, B) :- edu_less(A, C), edu_le3(C, B). 

Notez que la réduction supplémentaire de la première règle est active uniquement lorsque A et B arriver à être le même terme. Maintenant que la coupe est une coupe verte appropriée, le prédicat fonctionne aussi dans les cas les plus généraux comme l'original:

?- edu_le3(A, A). 
true. 

?- edu_le3(A, B). %note that A and B are not the same term 
A = B ; 
A = hs, 
B = college ; 
A = hs, 
B = masters ; 
A = hs, 
B = phd ; 
A = college, 
B = masters ; 
A = college, 
B = phd ; 
A = masters, 
B = phd ; 
false. 

?- 

avec Prolog retours en arrière à travers toutes les solutions.

Je ne pense pas qu'il existe un moyen d'éliminer le dernier false sans introduire trop de dépendance sur edu_lt. Ceci parce que nous devons garder ouvert la possibilité qu'il y a un autre edu_lt à explorer, dans le cas où vous décidez plus tard de l'enrichir avec plus de faits au sol. Donc, à mon avis, c'est le meilleur que vous pouvez avoir.

[1] Manuel de référence SWI Prolog, section 4.1.