Prolog 如何使用dif/2防止生成序列中的重复?

Prolog 如何使用dif/2防止生成序列中的重复?,prolog,clpfd,failure-slice,prolog-dif,Prolog,Clpfd,Failure Slice,Prolog Dif,这个问题是在回答一个位的泛化问题时提出的,该位生成由一组有限元素组成的所有序列,并且没有重复出现 正如Boris在评论中正确指出的那样,这个问题有许多现有的解决方案。然而,我感兴趣的是一种解决方案,它不使用累加器,即一个已经拾取的元素列表,与新选择的元素进行比较,而是使用dif/2语句 为了举例说明,在我下面的程序中,我有4个元素,在4次递归调用之后,还有两个div/2语句,它们声明到目前为止选择的4个元素是成对不同的。由此可以推断,继续递归并查找第五个元素是没有意义的,因为给定div/2语句,

这个问题是在回答一个位的泛化问题时提出的,该位生成由一组有限元素组成的所有序列,并且没有重复出现

正如Boris在评论中正确指出的那样,这个问题有许多现有的解决方案。然而,我感兴趣的是一种解决方案,它不使用累加器,即一个已经拾取的元素列表,与新选择的元素进行比较,而是使用dif/2语句

为了举例说明,在我下面的程序中,我有4个元素,在4次递归调用之后,还有两个div/2语句,它们声明到目前为止选择的4个元素是成对不同的。由此可以推断,继续递归并查找第五个元素是没有意义的,因为给定div/2语句,就没有元素了。有没有办法将这些“知识”编码到程序中,使其不再循环

:- use_module(library(apply)).
:- use_module(library(dif)).

sequences([]).
sequences([H|T]):-
  maplist(dif(H), T),
  between(1, 4, H),
  sequences(T).
当前循环行为:

?- sequences(X).
X = [] ;
X = [1] ;
...
X = [4, 3, 1, 2] ;
X = [4, 3, 2, 1] ;
<LOOP>

不是真正的答案,但太长,无法发表评论:

你的问题是你想让你的元素成为事实。将它们放在一个列表中,您可以使用select/3从该列表中取出一个元素。只要你把它们当作事实,我觉得这会比它需要的更全面。基数有顺序的排序列表,通常是在Prolog中表示集合的一种非常好的方法

编辑:

由于我仍然不确定我是否理解你的问题,以下是我认为你所问问题的实际答案:

不要使用dif/2,因为它不是必需的。相反,例如:

combination([], _).
combination([X|Xs], Set) :-
    select(X, Set, Set0),
    combination(Xs, Set0).
在这里,您必须使用setof/3或另一个构建所有解决方案列表的谓词来构建初始集。如果您可以使用列表并从中选择/3,我看不到dif/2的必要性

但如果你真的坚持,我会这样做:

set_el(a).
set_el(b).
set_el(c).

set_el_combination(Combination) :-
    set_el_combination_1([], Combination).

set_el_combination_1(C, R) :-
    reverse(C, R).
set_el_combination_1(C0, C) :-
    maplist(dif(X), C0),
    set_el(X),
    set_el_combination_1([X|C0], C).
您会注意到,解决方案的顺序不同于预期的正确词典顺序。如果希望避免在结尾处反转,可以使用差异列表。我相信这也可以写成DCG

这有帮助吗?

首先是一个小问题——名称:sequences/1表示序列列表无论序列是什么,它都应该是sequence/1

您需要的是相当多的糟糕的Prolog系统:您需要更强的一致性。我想,无论如何

我的即时反应是使用libraryclpfd!不起作用,让我们看看为什么

?- length(Xs,N),Xs ins 1..4, all_distinct(Xs).
它的循环与您的版本一样多,这可以通过以下内容看到:

我们最亲爱的声明海报儿童地图列表/2陷入了赤裸裸!好吧,也许我们不应该那么苛刻。毕竟,谓词的诚实不终止总是比肆无忌惮的不健全或不完整的攻击更可取

我们需要了解的是,所有_distinct/1都需要知道列表的长度,并且所有域也必须存在

sequence(Xs) :-
   sequence_aux(Xs, []).

sequence_aux([], _).
sequence_aux([X|Xs], Ys) :-
   X in 1..4,
   all_distinct([X|Ys]),
   sequence_aux(Xs, [X|Ys]).

 ?- sequence(X). 
现在结束

@mat可能会注意到,可能会删除所有_distinct[u]。也许还不止这些

如果您不喜欢此解决方案,因为它使用了一个额外的参数,则需要实现一个更安全的maplist/2

现在您可以一字不差地使用原始程序。但我觉得自己做得不太好。理解冻结程序中不终止的精确边界要困难得多


顺便说一句:您可能会尝试在SWI中获得正确的变量名来替换答案,因为类似于G772的编号不允许将答案重新粘贴回顶级shell并获得正确的结果。

是否只希望列表长度为2或更长,或者你想要一个通用的解决方案?@Boris我希望这个程序能够处理任意长度/任意数量的元素基数的列表。2的下界确实是不必要的,并且可以推广。这难道不等同于:对于一组大小为N的集合,找到大小为0或1或2的所有组合。。。高达N?还是我遗漏了什么?@Boris我同意在这里使用累加器是正确的解决方案。我在解决最初的问题时使用了它,这就引出了当前的问题。我的观点是,我知道有-say-N个唯一元素,并且我知道这些元素是两两不同的,即dif/2语句,因此我的程序不应该循环,因为它已经有足够的知识。@Boris我相信您是正确的,我的问题与您描述的问题相同。事实上,你的更一般。如果它可以用dif/2解决,并且没有累加器,那么它也会自动回答我的问题。根据你的建议,我已经能够改进我的问题的公式,减少DCG以及2个或更多的要求。我也不再将这些要素视为事实。我希望我的问题现在更清楚一点。 sequences([]) :- false. sequences([H|T]):- maplist(dif(H), T), false between(1, 4, H), sequences(T). ?- sequences(X), false.
sequence(Xs) :-
   sequence_aux(Xs, []).

sequence_aux([], _).
sequence_aux([X|Xs], Ys) :-
   X in 1..4,
   all_distinct([X|Ys]),
   sequence_aux(Xs, [X|Ys]).

 ?- sequence(X). 
fmaplist(C_1, Xs) :-
    freeze(Xs, fmaplist_aux(C_1, Xs)).

fmaplist_aux(_C_1, []).
fmaplist_aux(C_1, [X|Xs]) :-
   call(C_1, X),
   freeze(Xs, fmaplist_aux(C_1, Xs)).