【问题标题】:How to implement a not_all_equal/1 predicate如何实现 not_all_equal/1 谓词
【发布时间】:2017-11-24 12:45:42
【问题描述】:

如何实现not_all_equal/1 谓词,如果给定列表包含至少 2 个不同的元素则成功,否则失败?

这是我的尝试(不是很纯粹的尝试):

not_all_equal(L) :-
    (   member(H1, L), member(H2, L), H1 \= H2 -> true
    ;   list_to_set(L, S),
        not_all_equal_(S)
    ).

not_all_equal_([H|T]) :-
    (   member(H1, T), dif(H, H1)
    ;   not_all_equal_(T)
    ).

然而,这并不总是具有最佳行为:

?- not_all_equal([A,B,C]), A = a, B = b.
A = a,
B = b ;
A = a,
B = b,
dif(a, C) ;
A = a,
B = b,
dif(b, C) ;
false.

在这个例子中,应该只给出第一个答案,另外两个是多余的。

【问题讨论】:

  • 我可能会称它为not_all_same,因为“等于”有一点数字含义。 :)

标签: list prolog predicate prolog-dif logical-purity


【解决方案1】:

这是使用library(reif)SICStus|SWI 的部分实现。这当然是正确的,因为当它无法继续时会产生错误。但它缺乏我们想要的普遍性。

not_all_equalp([A,B]) :-
   dif(A,B).
not_all_equalp([A,B,C]) :-
   if_(( dif(A,B) ; dif(A,C) ; dif(B,C) ), true, false ).
not_all_equalp([A,B,C,D]) :-
   if_(( dif(A,B) ; dif(A,C) ; dif(A,D) ; dif(B,C) ; dif(B,D) ), true, false ).
not_all_equalp([_,_,_,_,_|_]) :-
   throw(error(representation_error(reified_disjunction),'C\'est trop !')).

?- not_all_equalp(L).
   L = [_A,_B], dif(_A,_B)
;  L = [_A,_A,_B], dif(_A,_B)
;  L = [_A,_B,_C], dif(_A,_B)
;  L = [_A,_A,_A,_B], dif(_A,_B)
;  L = [_A,_A,_B,_C], dif(_A,_B)
;  L = [_A,_B,_C,_D], dif(_A,_B)
;
! error(representation_error(reified_disjunction),'C\'est trop !')

?- not_all_equalp([A,B,C]), A = a, B = b.
   A = a,
   B = b
;  false.

编辑:现在我意识到我根本不需要添加那么多dif/2 目标! one 变量与第一个变量不同就足够了!无需互斥!删除dif(B,C) 目标我还是觉得有点不安全……

not_all_equalp([A,B]) :-
   dif(A,B).
not_all_equalp([A,B,C]) :-
   if_(( dif(A,B) ; dif(A,C) ), true, false ).
not_all_equalp([A,B,C,D]) :-
   if_(( dif(A,B) ; dif(A,C) ; dif(A,D) ), true, false ).
not_all_equalp([_,_,_,_,_|_]) :-
   throw(error(representation_error(reified_disjunction),'C\'est trop !')).

答案是完全一样的......这里发生了什么,我想。这个版本是不是更弱,更不一致性?

【讨论】:

  • IMO ?- dif(X,Y,T).真正受益于与( T = true, dif(X,Y) ; T = false, X=Y ) 相同的解决方案顺序!
  • 其实这超出了dif/3;这是我想提出的具体谓词的编码约定。为什么? (1) 它以直观的方式为library(reif) 用户提供了更多控制权。 (2) 它强调与常规Prolog析取的相似性。 (3) 答案序列将开始相同。其他答案(使用(->)/2if/3 时缺少)将不会预先显示。根据“沟通101”:要达成协议,首先强调共同点,然后然后扩展它(反之亦然)......这对你有意义吗?会写出这个问题的答案。
【解决方案2】:

这是一种简单的方法,您可以保留

not_all_equal([E|Es]) :-
   some_dif(Es, E).

some_dif([X|Xs], E) :-
   (  dif(X, E)
   ;  X = E, some_dif(Xs, E)
   ).

以下是一些使用 SWI-Prolog 7.7.2 的示例查询。

首先,最一般的查询:

?- not_all_equal(Es).
   dif(_A,_B), Es = [_A,_B|_C]
;  dif(_A,_B), Es = [_A,_A,_B|_C]
;  dif(_A,_B), Es = [_A,_A,_A,_B|_C]
;  dif(_A,_B), Es = [_A,_A,_A,_A,_B|_C]
;  dif(_A,_B), Es = [_A,_A,_A,_A,_A,_B|_C]
...

接下来,OP在问题中给出的查询:

?- not_all_equal([A,B,C]), A=a, B=b.
   A = a, B = b
;  false.                          % <- the toplevel hints at non-determinism

最后,让我们把子目标A=a, B=b放在前面:

?- A=a, B=b, not_all_equal([A,B,C]).
   A = a, B = b
;  false.                          % <- (non-deterministic, like above)

很好,但理想情况下,最后一个查询应该确定性地成功!


输入@987654322@(@987654323@)

First argument indexing 将第一个谓词参数的主要函子(加上一些简单的内置测试)考虑在内,以提高充分实例化目标的确定性。

就其本身而言,没有令人满意地覆盖dif/2

我们能做什么?一起工作 reified term equality/inequality——实际上是indexing dif/2

some_dif([X|Xs], E) :-                     % some_dif([X|Xs], E) :-
   if_(dif(X,E), true,                     %    (  dif(X,E), true
       (X = E, some_dif(Xs,E))             %    ;  X = E, some_dif(Xs,E)
      ).                                   %    ).

注意新旧实现的相似之处!

在上面,目标X = E 在左侧是多余的。让我们删除它!

some_dif([X|Xs], E) :-
   if_(dif(X,E), true, some_dif(Xs,E)).

太棒了!但是,唉,我们还没有完成(还)!

?- not_all_equal(Xs)。 不会终止

发生了什么事?

事实证明,dif/3 的实现使我们无法为最一般的查询获得良好的答案序列。为此——在不使用强制公平枚举的额外目标的情况下——我们需要对dif/3 进行调整,我称之为diffirst/3

diffirst(X, Y, T) :-
   (  X == Y -> T = false
   ;  X \= Y -> T = true
   ;  T = true,  dif(X, Y)
   ;  T = false, X = Y
   ).

让我们在谓词some_dif/2的定义中使用diffirst/3而不是dif/3

some_dif([X|Xs], E) :-
   if_(diffirst(X,E), true, some_dif(Xs,E)).

所以,终于有了上面的新some_dif/2查询:

?- not_all_equal(Es).                     % query #1
   dif(_A,_B), Es = [_A,_B|_C]
;  dif(_A,_B), Es = [_A,_A,_B|_C]
;  dif(_A,_B), Es = [_A,_A,_A,_B|_C]
...

?- not_all_equal([A,B,C]), A=a, B=b.      % query #2
   A = a, B = b
;  false.

?- A=a, B=b, not_all_equal([A,B,C]).      % query #3
A = a, B = b.

查询 #1 不会终止,但具有相同的简洁紧凑的答案序列。 好!

查询 #2 仍然不确定。好的。对我来说,这是最好的。

查询 #3 已成为确定性:现在更好!


底线:

  1. 使用library(reif) 驯服过多的不确定性,同时保持逻辑纯度!
  2. diffirst/3 应该可以进入 library(reif) :)



编辑:更一般地使用(由评论建议;谢谢!)

让我们这样概括some_dif/2

:- meta_predicate some(2,?).
some(P_2, [X|Xs]) :-
   if_(call(P_2,X), true, some(P_2,Xs)).

some/2 可以与除diffirst/3 之外的具体谓词一起使用。

这里是not_all_equal/1 的更新,现在使用some/2 而不是some_dif/2

not_all_equal([X|Xs]) :-
   some(diffirst(X), Xs).

上面的示例查询仍然给出相同的答案,所以我不会在这里展示这些。

【讨论】:

  • 由于某种原因,在列表的第一个元素上应用差异是令人困惑的,但在考虑更多之后它确实涵盖了所有情况......不应该过度考虑它!
  • 这也让我大吃一惊!现在回头看,很明显,是对原始问题陈述的直接翻译。
  • 漂亮而恰当的过度概括!但是,是您认为dif/3 应该以不同方式列举答案的原因吗?我怀疑还有另一个谓词,另一个顺序更好。枚举恰好适用于您的方法,这纯属巧合。
  • some_dif(Es, E). 改写成some(Es, dif(E)) 怎么样?
  • ... 或者更确切地说是some(dif(E), Es)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 2014-01-26
  • 1970-01-01
  • 2014-04-01
  • 2016-06-23
相关资源
最近更新 更多