【问题标题】:alldifferent/1 predicate in PrologProlog 中的 alldifferent/1 谓词
【发布时间】:2015-08-01 05:04:49
【问题描述】:
allsame([]).
allsame([X]).
allsame([X,X|Z]) :-
   allsame([X|Z]).

如何更改才能得到下面的结果?

alldifferent(L): The elements in L are all different.

?- alldifferent([a, b, b, c, d]).
false
?- alldifferent([a, b, c, d, e]).
true

【问题讨论】:

  • 通过使用搜索工具.. stackoverflow.com/questions/20131904/…
  • 这个问题可以通过询问最有效的计算方法来变得更有趣。我不确定length/1-方法是否比处理排序列表(有重复项)更快/更慢。
  • 您是否正在寻找有关如何创建 alldifferent/1 的理解,或者您是否真的特别想知道它与 allsame/1 的关系(在另一个问题中给出的答案)以及具体如何allsame/1 可以变形为alldifferent/1?找到像alldifferent/1 这样的谓词的解决方案可能看起来根本不像解决allsame/1。此外,您应该尝试将到目前为止所学的知识应用到问题中。首先写下您认为列表成员“完全不同”的逻辑含义,然后将其翻译为 Prolog。
  • @magus@mbratch 谢谢你们俩。我想我找到了解决方案。那时我只是在寻找 alldifferent/1 谓词,但现在,我想将它与 allsame/1 联系起来。再次感谢你:)

标签: prolog dcg


【解决方案1】:

首先,让我们看看您对allsame/1 的定义。对于给定的列表, 你正在建立这样的平等:

[ A,      B,      C,      D ... ]
    A = B,  B = C,  C = D,   ...      chain pattern

所以你正在建立一个平等的链条。还有另一种表达方式,通过引用一个公共变量来代替:

[ A,      B,      C,      D ... ]
  A = V,  B = V,  C = V,  D = V,  ... star pattern

这是最惯用的表达方式:

allsame_bis(L) :-
   maplist(=(_), L).

或者,更冗长,不使用经常定义的maplist/2

allsame_ter(L) :-
   allsame_with(L, _).

allsame_with([], _).
allsame_with([X|Xs], V) :-
   X = V,                      % could be moved into the head
   allsame_with(Xs, V).

现在,你说,你“想把它与alldifferent/1 联系起来”,这让情况变得更加复杂。请注意,alldifferent/1 不是对您的财产的直接否定。那将是:

notallsame(L) :-
   phrase((..., [A,B], {dif(A,B)}, ...), L),

有两个直接连续的不同元素。

为了完整起见,这里有一个版本可以避免像notallsame([1,2,3])这样的查询出现冗余答案:

notallsame_bis(L) :-
   phrase((all(=(A)),[A,B], {dif(A,B)}, ...), L).

首先是一系列相同的元素,然后是一个不同的元素。

all//1... //1defined elsewhere

但回到 alldifferent/2。等式关系是可传递的,它允许我们做一些捷径,要么做链,要么做星。但是不同是不及物的。所以我们现在必须在所有可能的对之间建立差异关系dif/2。总的来说,我们需要 n2-n/2 多个 dif/2 目标。嘿,让我们高兴的是我们仍然可以利用交换性,否则我们将不得不支付两倍以上的费用。

 alldifferent([]).
 alldifferent([X|Xs]) :-
    maplist(dif(X), Xs),
    alldifferent(Xs).

这个关系是这样建立dif/2的:

 [ A,      B,        C,        D,        E ... ]
       dif(B,A), dif(C,A), dif(D,A), dif(E,A), ... maplist(dif(A),[B,C,D,E ...])
                 dif(C,B), dif(D,B), dif(E,B), ... maplist(dif(B),  [C,D,E ...])
                           dif(D,C), dif(E,C), ... maplist(dif(C),    [D,E ...])
                                     dif(E,D), ... maplist(dif(D),      [E ...])

这是另一个版本,它可能看起来更简单。实际上,它与您的原始程序有一定的相似之处。没有吗?

alldifferent_bis([]).
alldifferent_bis([_]).
alldifferent_bis([A,B|Xs]) :-
   dif(A,B),
   alldifferent_bis([A|Xs]),
   alldifferent_bis([B|Xs]).

所以最后,我们可以通过a higher order definition 使用以下定义:

alldifferent_ter(L) :-
   pairwise(dif,L).

allsame_quater(L) :-
   pairwise(=,L).

如果您出于某种原因无法使用 dif/2,请改用安全的 ISO Prolog 近似值 iso_dif/2。它会尽可能多地(安全地)成功,但是当安全失败可能时,它可能会产生错误。想想alldifferent_bis([_,a,a])

【讨论】:

  • @mbratch:谢谢!我的潜意识说:手动优化,我的理由是:[B|Xs] 应该被编译器检测为一个常见的子项 - 好吧,系统现在不这样做,但是......
  • 感谢iso_dif/2 的提示。我有点感叹 GNU 没有dif/2
  • @mbratch:添加警告。
  • SO的每一个答案都应该是这样的。干得好!
  • 现在可以了吗?完全糊涂了。
【解决方案2】:

这是一种方法:

all_different( Xs ) :-
  all_different(Xs,[])
  .

all_different( []     , _ ) .  % if the source list is exhausted, success!
all_different( [X|_]  , S ) :- % if the list is non-empty
  member(X,S ) ,               % - if we've already seen X
  ! ,                          % - we cut and fail:
  fail                         % The list has duplicates
  .                            %
all_different( [X|Xs] , S ) :- % otherwise, we simply recurse down,
  all_different( Xs , [X|S] )  % prepending X to the "already seen" list.
  .                            % Easy!

另一种方式:

all_different( Xs ) :- setof( X , member(X,Xs), Xs ) .

第三种方式:

all_different( Xs ) :-
  msort(Xs,S) ,
  check_uniqueness(S)
  .

check_uniqueness([]) .
check_uniqueness([X,X|_]) :- !, fail .
check_uniqueness([_|Xs])  :- check_uniqueness(Xs) .

第四种方式:

all_different( Xs ) :-
  findall( X , (append(Pfx,[X|Sfx],Xs),(member(X,Pfx);member(X,Sfx))) , [] )
  .

有不止一种方法可以做到这一点……有些方法比其他方法更有效。

【讨论】:

  • 您所说的第三种方式总是会成功,因为sort/2 删除了重复项。将sort 更改为msort
  • 每个版本的行为似乎不同。
【解决方案3】:

另一种方法:

all_different(Xs) :-
    sort(Xs, Sorted),
    msort(Xs, Sorted).

SWI-Prolog 风格的模式声明:

%! all_different(+Xs:list) is semidet.

【讨论】:

  • all_different([X,a]), X = a 现在成功了。要么失败,要么产生错误。
  • @false 不打算在参数未完全实例化时使用。我添加了模式声明来显示这一点。
  • 以上声明排除了all_different([a,b|_]),但不排除all_different([X,a])。而且,如果可以的话,all_different([+X,+a]) 呢?
  • 整个句子内容为:参数必须完全实例化为满足类型的术语。并且 [_] 为list 完全实例化。
  • 请再读一遍句子:它说:完全实例化为满足类型的术语。您的读数将表示为:地面。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多