web-dev-qa-db-fra.com

Comment supprimer les doublons d'une liste dans SWI-Prolog?

J'ai donc besoin d'écrire un prédicat remove_duplicates/2 qui supprime les éléments en double d'une liste donnée. Par exemple:

?- remove_duplicates([a,a,b,c,c], List). List = [a,b,c] Yes

N'oubliez pas que j'apprends SWI-Prolog depuis deux jours et que je ne comprends que les bases de Prolog. Voici ce que j'ai pour le moment:

remove_duplicates([H | T], List) :- member(H, T), append(T, [], List1).

Cela fonctionne pour la liste [a,a,b,c] mais pas pour les listes où deux éléments de la queue sont identiques. Je me suis dit que je devais en quelque sorte enlever la tête à une liste temporaire, créer une nouvelle tête et répéter le prédicat. Je ne sais pas comment faire ça. De plus, lorsque la tête n'est pas dans la queue, par exemple avec des listes telles que [a,b,b,c,], le terminal dit simplement False, car member(H, T) n'est pas vrai.

Des idées?

7
Marthijn

Pour supprimer les doublons, si l'ordre n'a pas d'importance, le plus simple consiste à utiliser sort/2:

?- sort([a,a,b,b,c], X).
X = [a, b, c].

?- sort([c,c,a,a,b], X).
X = [a, b, c].

Bien sûr, vous voyez que l'ordre d'origine des éléments est perdu. Plus important encore, cela ne garantit d'être correct que si la liste que vous triez est déjà moulue (aucune variable libre ne s'y trouve). Considérez ce petit exemple:

?- sort([X,Y], [a,a]).
X = Y, Y = a.

Ne vous sentez pas parfaitement à l'aise si votre objectif est de supprimer les doublons ....

Donc, vous pourriez aussi bien avoir écrit:

must_be(ground, List), sort(List, Unique).

Vous pouvez également le faire vous-même et conserver l'ordre d'origine. Mais alors, vous devez garder trace des éléments que vous avez vus jusqu'à présent. Par exemple, vous pouvez les conserver dans une liste supplémentaire:

La liste des éléments vus jusqu'à présent est vide au début

list_unique(List, Unique) :-
    list_unique_1(List, [], Us).

list_unique_1([], _, []).
list_unique_1([X|Xs], So_far, Us) :-
    list_unique_2(X, Xs, So_far, Us).

Si tous les éléments vus jusqu’à présent sont différents de X, mettez-le dans la liste des éléments uniques et ajoutez-le à la liste des éléments vus jusqu'à présent.

list_unique_2(X, Xs, So_far, [X|Us]) :-
    maplist(dif(X), So_far),
    list_unique_1(Xs, [X|So_far], Us).

Si l'élément a déjà été vu, ignorez-le.

list_unique_2(X, Xs, So_far, Us) :-
    memberchk(X, So_far),
    list_unique_1(Xs, So_far, Us).

C'est la façon la plus simple de le faire. Il existe d'autres moyens plus intelligents de le faire qui pourraient avoir une meilleure complexité, mais c'est la plus facile à écrire.

6
user1812457

Voici quelques problèmes avec votre prédicat:

remove_duplicates([H | T], List) :-
  member(H, T),
  append(T, [], List1).

D'abord, vous devriez avoir un prédicat récursif, vous recherchez la tête de liste mais vous ne vérifiez pas récursivement la queue de la liste:

afin que vous puissiez le changer en:

remove_duplicates([H | T], List) :- 
      member(H, T),
      remove_duplicates( T, List).

Ce qui précède appelle récursivement pour la queue.

Deuxièmement, vous devez décider de ce qui se passera si h n'est pas membre de la liste, vous devez donc ajouter une autre clause:

remove_duplicates([H | T], [H|T1]) :- 
       \+member(H, T), 
       remove_duplicates( T, T1).

Ce qui précède vérifie si H existe dans T et si ce n’est pas le cas, force le premier élément de la liste que vous voulez retourner à être l’élément H:

si ce n'est pas si clair, ce qui précède est égal à ceci:

  remove_duplicates([H | T], List) :- 
           \+member(H, T), 
           List=[Head|Tail],
           Head=H,
           remove_duplicates( T, Tail).

Ce n’est pas très élégant, c’est juste pour comprendre comment fonctionne le précédent.

Enfin, vous avez besoin d’une base à votre récurrence pour la liste vide:

remove_duplicates([],[]).

Ainsi, le prédicat remove_duplicates selon les clauses ci-dessus devrait être:

remove_duplicates([],[]).

remove_duplicates([H | T], List) :-    
     member(H, T),
     remove_duplicates( T, List).

remove_duplicates([H | T], [H|T1]) :- 
      \+member(H, T),
      remove_duplicates( T, T1).

Bien que vous deviez remarquer que ce qui précède ne garderait qu'une fois chaque élément par exemple

remove_duplicates([a,a,b,c],L).
L = [a, b, c] ;
false.

C'est ok mais vérifiez l'exemple ci-dessous:

remove_duplicates([a,a,b,a,c],L).
L = [b, a, c] ;
L = [b, a, c] ;
false.

Cela retournera L = [b, a, c] car cela effacerait les deux premiers 'a' de la liste et ne garderait que le troisième.

Si vous souhaitez effacer uniquement les doublons, ce qui précède ne fonctionnera pas à cause du prédicat du membre . Vous pouvez écrire:

remove_duplicates2([],[]).

remove_duplicates2([H],[H]).

remove_duplicates2([H ,H| T], List) :-remove_duplicates( [H|T], List).

remove_duplicates2([H,Y | T], [H|T1]):- Y \= H,remove_duplicates( [Y|T], T1).

Le prédicat remove_duplicates2 est similaire à remove_duplicates mais avec quelques différences importantes:

1) Vous n'utilisez pas membre, mais vous examinez ce qui se passe lorsque vous avez une liste [H, H | T] avec deux éléments SAME et que vous n'en gardez qu'un, vous ignorez donc le premier H et appelez récursivement remove_duplicates2 ([H | T], L).

2) Si vous avez entré [H, Y | T] (une liste avec au moins deux éléments H, Y et une queue de la liste) et H\= Y, vous appelez remove_duplicates2 ([Y | T], T1) et vous avez stocké H dans la liste que vous souhaitez renvoyer.

3) Enfin, parce que toutes les clauses nécessitent au moins deux éléments, votre base de récurrence est la liste avec un élément: remove_duplicates2 ([H], [H]).

4) La clause remove_duplicates2 ([], []). est utilisé uniquement si l'entrée est la liste vide, il est donc nécessaire de couvrir cette entrée.

Le prédicat remove_duplicates2 vous donnerait:

 remove_duplicates2([a,a,b,a,c],L).
L = [a, b, a, c] ;
false.

remove_duplicates2([a,a,b,c],L).
L = [a, b, c] ;
false.
2
coder

Essayez ceci, j'espère que cela vous aidera:

delete3(_,[],[]).
delete3(X,[X|T],R):- delete3(X,T,R).
delete3(X,[H|T],[H|R]) :- delete3(X,T,R).

remove_duplicates([],[]).
remove_duplicates([H|T], [H|R]) :- 
    member(H,T),!,
    delete3(H,T,R1),
    remove_duplicates(R1,R).

remove_duplicates([H|T],[H|R]):-
    remove_duplicates(T,R).

Dans le second prédicat, nous vérifions si la variable d'en-tête * (H) * est membre de la queue de la liste * (T) *. Si c'est le cas, nous appelons le premier prédicat qui supprime toutes les variables égales à la variable head qui se trouve dans la queue, etc.

0
Sara Popa
% remdup(List, List_No_duplicates).
remdup([],[]).
remdup([H|T],Lnd):- rd1(H,T,T1), remdup(T1,Tnd),Lnd=[H|Tnd].
% rd1(X,L,Lr) removes all occurance of X in List L and store result in List Lr
rd1(_,[],[]).    
rd1(X,[X|T],Lx):-    rd1(X,T,Lx).
rd1(X,[Y|T],Lx):-    rd1(X,T,Lx1),Lx=[Y|Lx1].

Comment ça marche - prédicat remdup

  1. Prendre le 1er élément H de la liste donnée et supprimer toutes ses occurrences dans la partie de queue T - stocker ce résultat dans la liste T1; ET

  2. Appelez remdup de manière récursive sur le magasin T1. Résultat dans Tnd; ET

  3. Rajoutez l’élément H à Tnd qui est en réalité la partie de queue T qui a été modifiée par l’appel récursif de remdup et qui est donc déjà libre de tous les doublons et ne contient aucune occurrence de H.

prédicat - rd1

  1. Suppression de toutes les occurrences et de tout élément d'une liste vide est une liste vide
  2. Si tête correspond à X - continue d'appeler à nouveau sur la queue
  3. si la tête ne correspond pas à X - continuez ensuite avec la queue; ET ajoutez la tête Y sans correspondance à la partie de queue modifiée, qui est maintenant libre de toutes les occurrences de X
0
Nelson aka SpOOKY

Vous pouvez utiliser list_to_set/2 ou sort/2 comme indiqué dans la réponse ci-dessous.

0
Anton Danilov