查找列表中的唯一项目

Bor*_*per 3 prolog

我正在尝试编写一个规则来决定一个项目是否X恰好出现在列表中L.

unique(X, [X|T]):- !, \+ member(X, T).
unique(X, [_|T]):- unique(X, T).
Run Code Online (Sandbox Code Playgroud)

该规则适用于确定值是否在列表中是唯一的,或者当我尝试使用unique(X, [1,2,3,1,3,2,5,4,3,8]).它在列表中获取唯一值时返回正如false.我所期望的那样(如member(X, list).:

X = 5 ;
X = 4 ;
X = 8 ;
Run Code Online (Sandbox Code Playgroud)

我是一个完全的初学者,我不知道我做错了什么.

fal*_*lse 7

你一直在使用削减和不安全的否定形式.两者都必须非常谨慎地使用.立即解决方法是保护您的程序免受不适用于以下情况的用途:

unique(X, Xs) :-
   iwhen(ground(X+Xs), your_unique(X, Xs)).
Run Code Online (Sandbox Code Playgroud)

这使用iwhen/2类似于when/2它不会延迟:

:- meta_predicate iwhen(+, 0).

iwhen(Cond, G_0) :-
   when(Cond, ( Called = true, G_0 ) ),
   ( var(Called) -> throw(error(instantiation_error,_)) ; true ).
Run Code Online (Sandbox Code Playgroud)

以上适用于系统提供when/2.以下是适用于任何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).
Run Code Online (Sandbox Code Playgroud)

另一方面,它总是非常令人沮丧地接收实例化错误,而不是真正的答案.所以,让我们的程序真的很纯粹!

你的第二条规则

unique(X, [_|Es]) :-
   unique(X, Es).
Run Code Online (Sandbox Code Playgroud)

以声明方式,从右到左(即:-一个?)

提供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).
Run Code Online (Sandbox Code Playgroud)

这是另一种使用方式tfilter/3:

unique(X, Es) :-
   tfilter(=(X), Es, [_]).
Run Code Online (Sandbox Code Playgroud)

最有效的可能是它使用以下if_/3library(reif):

unique(X, [E|Es]) :-
   if_(X = E, non_member(E, Es), unique(X, Es) ).
Run Code Online (Sandbox Code Playgroud)

  • @Fatalize:我看到唯一的矫枉过正是`iwhen/2`并不容易获得. (2认同)

Fat*_*ize 6

这是一个简单的解决方案,使用nth0/4(或select/3如@false指出的):

unique(X, L) :-
    nth0(_, L, X, R),
    \+ member(X, R).
Run Code Online (Sandbox Code Playgroud)

nth0/4第四个参数是删除了元素的R列表。我们只需检查它不在 中。LXXR

更好的版本

unique(X, L) :-
    nth0(_, L, X, R),
    maplist(dif(X), R).
Run Code Online (Sandbox Code Playgroud)

这解决了@false 指出的问题,但由于您是初学者,我怀疑您对此是否感兴趣。

这样做的优点是在这样的情况下工作:

?- unique(b, [X, Y, a]).
X = b,
dif(Y, b) ;
Y = b,
dif(X, b) ;
false.
Run Code Online (Sandbox Code Playgroud)

  • @false 添加了改进版本。 (2认同)
  • 您的第二个版本非常好,因为它只是重用现有的递归谓词,但本身并不是递归的。然而,这种优雅是需要付出(程序上)代价的!`unique(a,[a,a|_])` 不会终止。 (2认同)
  • 小评论: [`select(X, L, R)`](http://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue#select) 更常见(我不知道`nth0 /4` 根本)。 (2认同)