你一直在使用剪切和不安全的否定形式。两者都必须小心使用。一个即时的修复方法是保护你的程序免受未设计的用途:
unique(X, Xs) :-
when_si(ground(X+Xs), your_unique(X, Xs)).
这使用when_si/2
(之前称为iwhen/2
),类似于when/2
,但它不延迟:
:- meta_predicate(when_si(+, 0)).
when_si(Cond, G_0) :-
when(Cond, ( Called = true, G_0 ) ),
( var(Called) -> throw(error(instantiation_error,_)) ; true ).
上述工作适用于提供
when/2
的系统。以下适用于任何符合ISO标准的系统:
when_si(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).
另一方面,每次都收到实例化错误而不是真正的答案,这真的很令人沮丧。所以,让我们让你的程序真正纯净起来!
你的第二个规则
unique(X, [_|Es]) :-
unique(X, Es).
以声明方式从右到左阅读(即:-
是←
)
假设X
是列表Es
的唯一元素,则X
也是列表[_|Es]
的唯一元素。
换句话说:只要我知道X
在Es
中是唯一的,它也将与Es
中的任何其他元素一起是唯一的。但这个结论并不正确,考虑通过添加X
来扩展列表!您需要一些额外的条件。另外,您的第一个规则需要重新表述。这使用了non_member/2
。
unique(X, [X|Es]) :-
non_member(X, Es).
unique(X, [E|Es]) :-
dif(X, E),
unique(X, Es).
这里是另一种使用 tfilter/3
的方法:
unique(X, Es) :-
tfilter(=(X), Es, [_]).
最高效的可能是以下使用if_/3
的library(reif)
:
unique(X, [E|Es]) :-
if_(X = E, non_member(E, Es), unique(X, Es) ).