免责声明:这是我自己的非正式和非评估课程。我自己尝试过,失败了,现在正在寻找一些指导。
我正在尝试实现一个版本的member / 2函数,该函数只返回一次列表的成员。
例如:
| ?- member(X, [1,2,3,1]).
X = 1 ? ;
X = 2 ? ;
X = 3 ? ;
X = 1 ? ;
我希望它只能打印出最多一次的每个数字。
| ?- once_member(X, [1,2,3,1]).
X = 1 ? ;
X = 2 ? ;
X = 3 ? ;
no
我们被告知用剪切'!'来做这件事操作员,但我查看了我的课程笔记和更多的在线,但仍然不能让它点击我的脑袋!
到目前为止,我已成功获得:
once_member(E, [E | L]) :- !.
once_member(E, [_, L]) :-
once_member(E, L).
返回1,然后没有别的,我觉得我的切割位置错误并且阻止了每次可能的匹配的回溯,但我真的不确定下一步该去哪里。
我查看了我的课程笔记以及http://www.cs.ubbcluj.ro/~csatol/log_funk/prolog/slides/5-cuts.pdf和Programming in Prolog (Google Books)
关于如何在逻辑上应用剪辑的指导将是最有用的,但答案可能有助于我自己解决这个问题。
我们也被告知要做另一种使用'\ +'否定失败的方法,但希望这一切可能更简单,一旦切割已经为我提供了支持?
答案 0 :(得分:2)
删除多余的答案和保持纯洁!
我们根据if_/3
and (=)/3
定义memberd/2
:
memberd(X, [E|Es]) :- if_(X = E, true, memberd(X, Es)).
特别是对于元谓词,有时候不同的参数顺序会派上用场:
list_memberd(Es, X) :-
memberd(X, Es).
示例查询:
?- memberd(X, [1,2,3,1]).
X = 1 ;
X = 2 ;
X = 3 ;
false.
答案 1 :(得分:1)
切割的解决方案......首先听起来很麻烦。
假设第一个参数将被实例化,解决方案是微不足道的:
once_member(X,L):-
member(X,L),!.
但如果第一个arg未实例化,则不会出现您想要的行为 如果我们知道列表元素的域(例如1到42之间的数字),我们可以实例化第一个参数:
once_member(X,L):-
between(1,42,X),
member_(X,L).
member_(X,L):-
member(X,L),!.
但这是低效率的
此时,我开始相信只用切割就不可能了(假设我们不使用+或list_to_set / 2
等一下! <在这里插入想法表情符号>
如果我们可以实现一个谓词(如list_to_set/2的swi-prolog),它将获取一个列表并生成一个列表,其中删除了所有重复元素,我们可以简单地使用普通成员/ 2并且不要得到重复的结果。尝试一下,我想你可以自己写一下。
--------掠夺者------------
one_member(X,L):-
list_to_set(L,S),
member(X,S).
list_to_set([],[]).
list_to_set([H|T],[H|S]):-
remove_all(H,T,TT),
list_to_set(TT,S).
%remove_all(X,L,S): S is L if we remove all instances of X
remove_all(_,[],[]).
remove_all(X,[X|T],TT):-
remove_all(X,T,TT),!.
remove_all(X,[H|T],[H|TT]):-
remove_all(X,T,TT).
如你所见,我们必须在remove_all / 3中使用cut,否则第三个子句可以由remove_all(X,[X|_],_)
匹配,因为我们没有指定H与X不同。我相信没有的解是不重要的。
remove_all(1,[1,2],[1,2])
也会成功。
另一方面,检查两次条件效率不高。因此,最佳的方法是使用if-then-else结构(但我假设您不允许使用它;它的实现可以通过剪切来完成)。
另一方面,还有另一个更简单的实现:不仅要检查X是否是列表的成员,还要检查先前是否遇到过它;所以你需要一个累加器:
-------------掠夺者--------------------
once_member(X,L):-
once_member(X,L,[]).
once_member(X,[X|_T],A):-
\+once_member(X,A).
once_member(X,[H|T],A):-
once_member(X,T,[H|A]).
答案 2 :(得分:1)
once_member(X, Xs) :-
sort(Xs, Ys),
member(X, Ys).
与发布的几乎所有其他解决方案一样,这有一些异常现象。
?- X = 1, once_member(X, [A,B]).
X = A, A = 1 ;
X = B, B = 1.
?- X = 1, once_member(X, [A,A]).
X = A, A = 1.
答案 3 :(得分:-1)
这是一种使用 once_member / 2 定义中的剪切以及经典成员/ 2 谓词的方法:
once_member(X,[H|T]) :-
member(H,T),
!,
once_member(X,T).
once_member(H,[H|_]).
once_member(X,[_|T]) :-
once_member(X,T).
应用于上面的例子:
?- once_member(X,[1,2,3,1]).
X = 2 ;
X = 3 ;
X = 1 ;
no
注意:尽管出现了奇怪的三个子句定义,但 once_member / 2 是最后调用/尾部递归优化,因为在此之前放置了切口它的第一次自我调用。