web-dev-qa-db-fra.com

Prolog - Les arguments ne sont pas suffisamment instanciés

J'écris un petit programme qui compte combien d'éléments dans une liste ne sont pas des nombres . Voici mon code:

not_number([],0).
not_number([X|T],R):- 
    not(number(X)),
    R1 is R+1,  
    not_number(T,R1). 

not_number([_|Tail],Result):-
    not_number(Tail,Result).  

Si j'exécute le code comme ceci:

?- not_number([1,2,3,5], R).

J'obtiens que R = 0 (comme il se doit)

R = 0.

Mais si je mets un personnage dans la liste:

?- not_number([1,2,3,5,a], R).

alors j'obtiens cette erreur:

ERROR: not_number/2: Arguments are not sufficiently instantiated
   Exception: (10) not_number([a], _G247) ? 

Quelqu'un peut expliquer ce qui ne va pas avec le code? Je suis nouveau au prologue.

16
Eddwhis

J'écris cette réponse, parce que la meilleure réponse à ce jour était dans un commentaire de lurker . Je veux qu'il apparaisse comme une réponse réelle.

Votre code ne fonctionne pas, car vous utilisez R1 is R+1 lorsque R n'est pas instancié dans le cas not_number([X|T], R). Votre cas récursif est un peu en arrière. Vous voulez faire ceci:

not_number([X|T],R):- 
    not(number(X)),
    not_number(T,R1),
    R is R1+1.

Maintenant, le côté droit de la is est instancié quand il est appelé.

15
Rialgar

Votre problème est que dans le calcul arithmétique comme ceci:

A est B

tout sur le côté droit (B) doit être déjà connu. Aucune variable ici.

Vous pouvez faire quelque chose comme ça:

not_number(X, Y) :- not_number(X, Y, 0).
not_number([], Y, Y).
not_number([H|T], Y, Z) :-
    \+ (number(H)), 
    Z1 is Z+1,
    not_number(T, Y, Z1).

not_number([H|T], Y, Z) :-
    number(H),
    not_number(T, Y, Z).

(testé ce code maintenant, ça marche).

Maintenant, le troisième argument est un accumulateur. Il compte combien de non-nombres il y a. Lorsque la liste est vide, ce troisième argument est unifié avec le second et devient la réponse appropriée.

Prolog, s'il en a l'occasion, empruntera toutes les voies possibles. Si vous faites quelque chose comme ça:

cat(adam).
cat(eve).

et ensuite demander:

?- cat(X).

Vous pouvez obtenir les deux réponses: X = adam et X = eve . Cela s'applique également à votre code: notez que, lorsque l'en-tête de la liste n'est pas un nombre, vous pouvez le faire:

not_number([_|Tail],Result):-
    not_number(Tail,Result).  

ce qui ne donne pas la réponse que vous voudriez. Vous devez couper les routes qui ne vous intéressent pas. Dans ce cas, j'ajouterais

number(Head).

pour nous assurer de sauter un élément dans une liste sans augmenter le compteur de 1 uniquement lorsque cet élément n'est pas un nombre.

Pour forcer Prolog à trouver d’autres résultats, vous devez appuyer sur ";" sur votre clavier (comme dans cet exemple d'Adam et Eve).

4
Darge Elar

La solution générale à de tels problèmes consiste à utiliser contraintes.

Par exemple, votre programme fonctionne exactement comme prévu si vous utilisez simplement clpfd contraintes. Il suffit de remplacer (is)/2 par (#=)/2 pour obtenir une arithmétique entière qui fonctionne dans toutes les directions:

:- use_module(library(clpfd)).

not_number([],0).
not_number([X|T],R):- 
    \+ number(X),
    R1 #= R+1,  
    not_number(T,R1). 

not_number([_|Tail],Result):-
    not_number(Tail,Result).

Exemple de requête et son résultat:

? - not_number ([1,2,3,5], R) .
R = 0.

Notez également que j'ai changé le code pour utiliser le prédicat ISO (\+)/1 au lieu de not/1.

0
mat