List Prolog-从列表中删除具有相同第一个值的对
我有这样的物品清单List Prolog-从列表中删除具有相同第一个值的对,list,prolog,List,Prolog,我有这样的物品清单 list([obj(x,y),obj(x,z),obj(a,b),obj(b,c)]). list([obj(a,b),obj(b,c)] 我想删除那些共享相同第一个值的元素,这样我就可以使用修改后的列表了。在本例中,最终列表如下所示 list([obj(x,y),obj(x,z),obj(a,b),obj(b,c)]). list([obj(a,b),obj(b,c)] 有人能帮忙吗?我真的很难做到这一点。让我们从测试开始吧 % Testing :- begin_
list([obj(x,y),obj(x,z),obj(a,b),obj(b,c)]).
list([obj(a,b),obj(b,c)]
我想删除那些共享相同第一个值的元素,这样我就可以使用修改后的列表了。在本例中,最终列表如下所示
list([obj(x,y),obj(x,z),obj(a,b),obj(b,c)]).
list([obj(a,b),obj(b,c)]
有人能帮忙吗?我真的很难做到这一点。让我们从测试开始吧
% Testing
:- begin_tests(collapse).
test(one) :- collapse([],[]).
test(two) :- collapse([obj(a,b)],[obj(a,b)]).
test(three) :- collapse([obj(a,b),obj(b,c)],
[obj(a,b),obj(b,c)]).
test(four) :- collapse([obj(a,b),obj(a,c),obj(b,j)],
[obj(b,j)]).
test(five) :- collapse([obj(a,b),obj(a,c),obj(b,j),obj(a,x),obj(b,y)],
[]).
test(six) :- collapse([obj(a,b),obj(a,c),obj(b,j),obj(b,y),obj(c,x)],
[obj(c,x)]).
:- end_tests(collapse).
rt :- run_tests(collapse).
然后编码:
% This is called
collapse(Lin,Lout) :- collapse(Lin,[],Lout).
/*
* Helper predicate:
* collapse(List_over_which_we_recur_getting_smaller,
* Elements_which_we_have_already_seen,
* List_which_collects_the_result_going_down,
* List_which_collects_the_result_coming_up).
*/
collapse([],_Filter,[]). % base case, kick a [] upwards; don't care about Filter
collapse([obj(A,_)|Objs],Filter,Lup) :-
(member(obj(A,_),Objs);member(obj(A,_),Filter)), % Does the obj(A,_) appear elsewhere (in Filter or Objs)?
!, % Commit to this execution path where obj(A,_) is not unique
(member(obj(A,_),Filter) % Slight improvement: add obj(A,_) to "Filter" only it it's not yet in there
-> NewFilter = Filter
; NewFilter = [obj(A,_)|Filter]),
collapse(Objs,NewFilter,Lup). % Do not retain obj(A,_)
collapse([obj(A,X)|Objs],Filter,Lup) :-
\+(member(obj(A,_),Objs);member(obj(A,_),Filter)), % Does the obj(A,_) appear elsewhere (in Seen or ToSee)?
!, % Commit to this execution path where obj(A,_) IS unique
collapse(Objs,Filter,Ltmp), % Filtering the rest of Objs, which defines Ltmp
Lup = [obj(A,X)|Ltmp]. % DO retain object on the way up, correctly ordering result.
好吧,那么:
?- rt.
% PL-Unit: collapse ...... done
% All 6 tests passed
true.
对于初学者来说,有效地解决这个问题并非易事。假设列表中的元素是ground,我们可以首先注意到,对列表进行排序将把在
obj/2
复合项中共享第一个参数的所有元素聚集在一起。例如:
| ?- sort([obj(x,y),obj(x,z),obj(a,b),obj(b,c)], S).
S = [obj(a, b), obj(b, c), obj(x, y), obj(x, z)]
yes
sort/2
是一个标准的内置谓词。任何合适的Prolog系统都应该以O(n*log(n))的复杂性来实现它。排序后,我们可以遍历列表,我们可以在O(n)中进行筛选:
filter(List, Filtered) :-
sort(List, Sorted),
walk(Sorted, Filtered).
walk([], []).
walk([obj(X,Y)| Sorted], Filtered) :-
walk(Sorted, X, obj(X,Y), Filtered).
walk([], _, Element, [Element]).
walk([obj(X,_)| Sorted], X, _, Filtered) :-
!,
delete(Sorted, X, Rest),
walk(Rest, Filtered).
walk([obj(X,Y)| Sorted], _, Element, [Element| Filtered]) :-
walk(Sorted, X, obj(X,Y), Filtered).
delete([], _, []).
delete([obj(X,_)| Sorted], X, Rest) :-
!,
delete(Sorted, X, Rest).
delete(Rest, _, Rest).
电话示例:
| ?- filter([obj(x,y),obj(x,z),obj(a,b),obj(b,c)], Filtered).
Filtered = [obj(a, b), obj(b, c)]
yes
看起来不错,但我们应该做更全面的测试。我们可以定义所有过滤器/2
谓词解决方案必须满足的属性:
property(List, Filtered) :-
filter(List, Filtered),
% all elements of the output list must
% be in input list
forall(
member(X, Filtered),
member(X, List)
),
% no two elements in the output list
% should share the first argument
\+ (
select(obj(X,_), Filtered, Rest),
member(obj(X,_), Rest)
),
% all elements in the input list whose
% first argument is not repeated must
% be in the output list
\+ (
select(obj(X,Y), List, Rest),
\+ member(obj(X,_), Rest),
\+ member(obj(X,Y), Filtered)
).
我们现在可以使用基于属性的测试实现,比如Logtalk的QuickCheck实现。但有一个陷阱。基于属性的测试要求我们能够生成包含obj/2
元素的列表。解决办法,我们作弊!首先,我们做一个从obj(X,Y)
到X-Y
的语法转换。此转换不会更改正在测试的谓词的语义:
filter(List, Filtered) :-
sort(List, Sorted),
walk(Sorted, Filtered).
walk([], []).
walk([X-Y| Sorted], Filtered) :-
walk(Sorted, X, X-Y, Filtered).
walk([], _, Element, [Element]).
walk([X-_| Sorted], X, _, Filtered) :-
!,
delete(Sorted, X, Rest),
walk(Rest, Filtered).
walk([X-Y| Sorted], _, Element, [Element| Filtered]) :-
walk(Sorted, X, X-Y, Filtered).
delete([], _, []).
delete([X-_| Sorted], X, Rest) :-
!,
delete(Sorted, X, Rest).
delete(Rest, _, Rest).
我们对属性/2
谓词应用相同的语法转换:
property(List, Filtered) :-
filter(List, Filtered),
% all elements of the output list must
% be in input list
forall(
member(X, Filtered),
member(X, List)
),
% no two elements in the output list
% should share the first argument
\+ (
select(X-_, Filtered, Rest),
member(X-_, Rest)
),
% all elements in the input list whose
% first argument is not repeated must
% be in the output list
\+ (
select(X-Y, List, Rest),
\+ member(X-_, Rest),
\+ member(X-Y, Filtered)
).
现在,我们可以使用以下目标进行测试:
| ?- lgtunit::quick_check(
property(
+list(pair(char,char)),
-list(pair(char,char))
)
).
% 100 random tests passed
% starting seed: seed(25256,26643,1563)
yes
注:在
property/2
谓词的定义中,我们假设事实上的标准member/2
和select/3
列表谓词在user
中可用(即在顶级解释器中)。如果不是这样,请在他们的呼叫前加上list::
Soo。。。保留索引最低的对象?签出:)有人说应该避免发表右边的评论,但它们确实有用;事实上,应该有专门的编辑器支持它们。Scheme/Lisp编码风格指南不要求在代码行内有额外的间距(Prolog可能也有),但代码是一种2D媒介,有时非常:(包括这里的链接只是为了说明。)我希望这能进入手册。@DavidTonhofer已经在那里:@DavidTonhofer在下面的博文中扩展了这个答案:谢谢Paulo。我希望不久能试试这个。