portable - prolog online
Encontrar elementos únicos en una lista (3)
Estoy tratando de escribir una regla que decide si un elemento X
ocurre exactamente uno en una lista L
unique(X, [X|T]):- !, /+ member(X, T).
unique(X, [_|T]):- unique(X, T).
La regla funciona para determinar si un valor es único dentro de una lista o no, pero cuando trato de obtener valores únicos dentro de una lista usando unique(X, [1,2,3,1,3,2,5,4,3,8]).
devuelve simplemente false.
Lo que esperaba es esto (como member(X, list).
X = 5 ;
X = 4 ;
X = 8 ;
Soy un principiante completo y no sé lo que estoy haciendo mal.
Aquí hay una solución simple usando nth0/4
(o select/3
como lo señala @false):
unique(X, L) :-
nth0(_, L, X, R),
/+ member(X, R).
nth0/4
4º argumento R
es la lista L
con el elemento X
eliminado. Simplemente comprobamos que X
no está en R
Mejor versión
unique(X, L) :-
nth0(_, L, X, R),
maplist(dif(X), R).
Esto soluciona el problema señalado por @false, aunque como eres un principiante dudo que esto sea de mucho interés para ti.
Esto tiene la ventaja de trabajar en situaciones como esta:
?- unique(b, [X, Y, a]).
X = b,
dif(Y, b) ;
Y = b,
dif(X, b) ;
false.
Esto suena como una pregunta de tarea.
Prueba algo como esto.
unique(M, L) :- member(M, L), count(M, L, 1).
count(M, [H|T], C) :- M = H, count(M, T, C1), C is C + 1.
...
Cuando se completa esto da ...
?- unique(X, [1,2,3,1,3,2,5,4,3,8]).
X = 5 ;
X = 4 ;
X = 8 ;
false.
Editar: Ya que las respuestas ya han sido dadas ... esta es la hábil.
unique(Item, List) :-
select(Item, List, L2),
/+ member(Item, L2).
Has estado usando cut y una forma insegura de negación. Ambos deben ser usados con extremo cuidado. Una solución inmediata sería proteger su programa de los usos para los que no está diseñado:
unique(X, Xs) :-
iwhen(ground(X+Xs), your_unique(X, Xs)).
Esto usa iwhen/2
que es similar a when/2
excepto que no se demora:
:- meta_predicate iwhen(+, 0).
iwhen(Cond, G_0) :-
when(Cond, ( Called = true, G_0 ) ),
( var(Called) -> throw(error(instantiation_error,_)) ; true ).
Arriba funciona para sistemas que proporcionan when/2
. A continuación es para cualquier sistema de conformidad ISO:
iwhen(Cond, G_0) :-
( when_condition(Cond)
-> ( Cond -> G_0 ; throw(error(instantiation_error,_)) )
; throw(error(domain_error(when_condition, Cond),_))
).
when_condition(C) :-
var(C),
!,
throw(error(instantiation_error,_)).
when_condition(ground(_)).
when_condition(nonvar(_)).
when_condition(?=(_, _)).
when_condition(( A, B )) :-
when_condition(A),
when_condition(B).
when_condition(( A ; B )) :-
when_condition(A),
when_condition(B).
Por otro lado, es bastante frustrante recibir errores de instanciación todo el tiempo en lugar de respuestas reales. Entonces, ¡hagamos que su programa sea realmente puro!
Tu segunda regla
unique(X, [_|Es]) :-
unique(X, Es).
lee declarativamente, de derecha a izquierda (que :-
es un ←
)
Siempre que
X
sea un elemento único de la listaEs
, entoncesX
es un elemento único de la lista[_|Es]
.
en otras palabras: cada vez que sé que X
es único en Es
, también será único con cualquier elemento adicional en Es
. Esa conclusión no es cierta, ¡considere extender la lista por X
! Necesitas alguna condición extra. Además, tu primera regla debe ser reformulada. Esto usa non_member/2
:
unique(X, [X|Es]) :-
non_member(X, Es).
unique(X, [E|Es]) :-
dif(X, E),
unique(X, Es).
Y aquí hay otra forma de usar tfilter/3
:
unique(X, Es) :-
tfilter(=(X), Es, [_]).
El más eficiente es probablemente el siguiente que usa if_/3
de la library(reif)
:
unique(X, [E|Es]) :-
if_(X = E, non_member(E, Es), unique(X, Es) ).