different(Xs, Ys) :-
member(X, Xs),
non_member(X, Ys).
different(Xs, Ys) :-
member(Y, Ys),
non_member(Y, Xs).
虽然这个使用member/2
和non_member/2
的定义几乎从声明的角度来看是 1 ,但它会为某些查询生成冗余解决方案,并留下选择点。
对此进行改进的定义是什么(以纯粹的方式使用if_/3
和(=)/3
),以便different/2
描述完全相同的解决方案集,但是在至少对于地面查询(因此不会留下任何无用的选择点)并省略(如果可能)任何多余的答案?
1 <子>
实际上,different([a|nonlist],[]), different([],[b|nonlist])
成功了。它同样可能失败。所以两者都失败的解决方案很好(甚至可能更精细)。
子>
答案 0 :(得分:14)
首先尝试!
以下代码基于元谓词tfilter/3
和tpartition/4
,单调的if-then-else控件构造if_/3
,统一的一元逻辑连接词not_t/3
,以及术语平等谓词(=)/3
:
different([],[_|_]).
different([X|Xs0],Ys0) :-
tpartition(=(X),Ys0,Es,Ys1),
if_(Es=[], true, (tfilter(not_t(=(X)),Xs0,Xs1),different(Xs1,Ys1))).
示例查询:
?- different([A,B],[X,Y]).
A=Y , dif(B,Y), X=Y
; A=X , B=X , dif(X,Y)
; A=X , dif(B,X), dif(B,Y), dif(X,Y)
; A=Y , B=Y , dif(X,Y)
; A=Y , dif(B,X), dif(B,Y), dif(X,Y)
; dif(A,X), dif(A,Y).
让我们在处理地面数据时观察确定性:
?- different([5,4],[1,2]).
true.
上述方法感觉就像是朝着正确方向迈出的一步......但是,按原样,我不会称之为完美。
答案 1 :(得分:14)
这是另一次尝试!我们利用单调if-then-else控件构造if_/3
,结合具体化列表成员谓词memberd_t/3
,以及第一个参数索引来避免创建无用的选择点。
different(Xs,Ys) :-
different_aux(Xs,Ys,Xs,Ys).
different_aux([],[_|_],Xs0,Ys0) :-
different_aux(Ys0,[],Ys0,Xs0). % swap Xs/Ys pair
different_aux([X|Xs],Ys,Xs0,Ys0) :-
if_(memberd_t(X,Ys0),
different_aux(Ys,Xs,Ys0,Xs0), % variant: different_aux(Xs,Ys,Xs0,Ys0)
true).
首先,我们运行一个我们期望失败的查询:
?- different([1,2,3],[2,3,1]).
false.
以下查询类似于上面给出的失败查询;每个人在第一个x
或第二个列表[1,2,3]
中的不同索引处放置一个“不同”商品[2,3,1]
:
?- different([4,2,3],[2,3,1]), different([1,2,3],[4,3,1]), different([1,4,3],[2,3,1]), different([1,2,3],[2,4,1]), different([1,2,4],[2,3,1]), different([1,2,3],[2,3,4]). true. % all subgoals succeed deterministically
好的!让我们运行我在我用过的另一个(非常一般的)查询 previous answer:
?- different([A,B],[X,Y]).
A=X , B=X , dif(Y,X)
; A=X , dif(B,X), dif(Y,B)
; A=Y , dif(B,X), dif(Y,X)
; dif(A,X), dif(A,Y).
紧凑!与我提出的相比有了很大改进 earlier!
答案 2 :(得分:11)
回到根源!这个变体非常接近到问题中OP给出的代码。
以下内容基于if_/3
和memberd_t/3
。
different(Xs,Ys) :-
if_(some_absent_t(Xs,Ys),
true,
some_absent_t(Ys,Xs,true)).
some_absent_t([] ,_ ,false).
some_absent_t([X|Xs],Ys,Truth) :-
if_(memberd_t(X,Ys), some_absent_t(Xs,Ys,Truth), Truth=true).
这是一个地面查询:
?- different([4,2,3],[2,3,1]), different([1,2,3],[4,3,1]), different([1,4,3],[2,3,1]), different([1,2,3],[2,4,1]), different([1,2,4],[2,3,1]), different([1,2,3],[2,3,4]). true. % all subgoals succeed deterministically
这是我在之前的答案中使用的(更一般的)查询:
?- different([A,B],[X,Y]).
A=X , B=X , dif(Y,X)
; A=X , dif(B,X), dif(B,Y)
; A=Y , B=Y , dif(Y,X), dif(Y,X)
; A=Y , dif(B,X), dif(B,Y), dif(Y,X)
; dif(A,X), dif(A,Y).
答案 3 :(得分:11)
(很多受@ repeat&#39; s last answer的启发,名字仍然太笨拙了)
different(Xs, Ys) :-
if_(tnotexists_inlist_t(list_memberd_t(Ys), Xs),
true,
tnotexists_inlist_t(list_memberd_t(Xs), Ys)).
tnotexists_inlist_t(_P_2, [], false).
tnotexists_inlist_t(P_2, [E|Es], T) :-
if_(call(P_2, E),
tnotexists_inlist_t(P_2, Es, T),
T = true).
答案 4 :(得分:8)
下一位参赛选手参加选美比赛! - )
这个答案显示了重构的代码变体 a previous answer。它使用具体的连接和分离:
and_(P_1,Q_1) :-
and_t(P_1,Q_1,true).
or_(P_1,Q_1) :-
or_t(P_1,Q_1,true).
and_t(P_1,Q_1,Truth) :-
if_(P_1, call(Q_1,Truth), Truth=false).
or_t(P_1,Q_1,Truth) :-
if_(P_1, Truth=true, call(Q_1,Truth)).
注意“and”和“or”两个版本;带有后缀_t
的那些对于真值具有额外的参数,没有后缀的那些没有并假设Truth=true
应该成立。
基于and_t/3
和有效术语不等式谓词dif/3
,我们定义nonmember_t/3
:
nonmember_t(X,Ys,Truth) :-
list_nonmember_t(Ys,X,Truth).
list_nonmember_t([] ,_, true).
list_nonmember_t([Y|Ys],X,Truth) :-
and_t(dif(X,Y), list_nonmember_t(Ys,X), Truth).
现在,让我们定义some_absent_t/3
,different_t/3
和different/2
,如下所示:
some_absent_t([] ,_ ,false). some_absent_t([X|Xs],Ys,Truth) :- or_t(nonmember_t(X,Ys), some_absent_t(Xs,Ys), Truth). different_t(Xs,Ys,Truth) :- or_t(some_absent_t(Xs,Ys), some_absent_t(Ys,Xs), Truth). different(Xs,Ys) :- different_t(Xs,Ys,true).
它仍然运行吗?
?- different([A,B],[X,Y]). A=X , B=X , dif(Y,X) ; A=X , dif(B,X), dif(B,Y) ; A=Y , B=Y , dif(Y,X), dif(Y,X) ; A=Y , dif(B,X), dif(B,Y), dif(Y,X) ; dif(A,X), dif(A,Y). % same result as before ?- different([4,2,3],[2,3,1]), different([1,2,3],[4,3,1]), different([1,4,3],[2,3,1]), different([1,2,3],[2,4,1]), different([1,2,4],[2,3,1]), different([1,2,3],[2,3,4]). true. % same result as before
看起来好的!
总而言之,不是对现有答案的巨大改进,而是IMO稍微更具可读性的代码,以及different/2
的具体版本作为额外奖励!
答案 5 :(得分:6)
在list_nonmember_t/3
,exists_in_t/3
的帮助下,让它达到极限
or_/2
!
some_absent_t(Xs,Ys,Truth) :-
exists_in_t(list_nonmember_t(Ys),Xs,Truth).
different(Xs,Ys) :-
or_(some_absent_t(Xs,Ys),
some_absent_t(Ys,Xs)).
答案 6 :(得分:6)
不久前提供了以下大胆赏金(+500):
这里仍然缺少一个惯用的答案。例如,or_t / 3应该是(;)/ 3。还有更多内容。
接受挑战!此答案是对this previous answer的跟进。
我们使用具体的逻辑连接词(;)/3
,可以这样定义:
';'(P_1,Q_1,T) :- if_(P_1, T=true, call(Q_1,T)).
接下来,我们定义meta-predicate call_/1
。它对于本答案中使用的具体谓词很有用。凭借其名称和语义,call_/1
跟在if_/3
,and_/2
和or_/2
之后!
call_(P_1) :- call(P_1,true).
使用(;)/3
,call_/1
和some_absent_t/3
我们实施different/2
:
different(As,Bs) :- call_((some_absent_t(As,Bs) ; some_absent_t(Bs,As))).
完成!那就是它。
让我们重新运行我们在之前的答案中使用的查询!
?- different([5,4],[1,2]). true. ?- different([1,2,3],[2,3,1]). false. ?- different([4,2,3],[2,3,1]), different([1,4,3],[2,3,1]), different([1,2,4],[2,3,1]), different([1,2,3],[4,3,1]), different([1,2,3],[2,4,1]), different([1,2,3],[2,3,4]). true.
相同的疑问,相同的答案......看起来好的给我!
答案 7 :(得分:3)
使用if_的Conerning解决方案,我想说另一种方法是从一开始就使用建设性的否定。 Constructive negation已经在80年代进行了研究,先驱者是David Chan,并且不时出现。
假设我们有一个Prolog解释器,它具有建设性的否定(~)/2
。这个建设性的否定(~)/2
可用于定义声明if-then-else如下,让我们调用此运算符(~->)/2
:
(A ~-> B; C) :- (A, B); (~A, C).
如果Prolog解释器除了建设性否定之外还嵌入了隐含(=>)/2
,那么可以选择定义声明if-then-else如下,让我们调用此运算符(~=>)/2
:
(A ~=> B; C) :- (A => B), (~A => C).
请注意从分离(;)/2
切换到联合(,)/2
。询问二元决策图(BDD)人员为什么它们在逻辑上是等价的。程序上存在细微差别,但是通过某些A的嵌入式暗示的后门,第二个if-then-else变体也将引入非确定性。
但是我们如何在Prolog解释器中进行并引入例如建设性否定。一种直接的方法是将建设性否定委托给约束求解器。如果约束求解器具有确定的否定,则可以按如下方式进行:
~ A :- #\ A.
但是没有那么多的约束求解器可以允许合理地使用member/2
等示例。因为它们通常仅为有限整数,有理数等域提供具体的否定。但是对于诸如member/2
之类的谓词,我们需要对Herbrand宇宙进行具体否定。
另请注意,建设性否定的常用方法也假设普通规则含义得到另一种解读。这意味着通常在建设性否定下,我们可以选择普通的member/2
定义,并获得查询结果,例如:
?- ~ member(X, [a,b,c]).
dif(X, a),
dif(X, b),
dif(X, c).
但是再次使用具体化的否定很难使用已定义的谓词,因此以下查询可能无效:
?- #\ member(X, [a,b,c]).
/* typically won't work */
如果上述成功,则任何声明性if-then-else如(~->)/2
或(~=>)/2
的使用频率较低,因为普通谓词定义已经由Prolog解释器提供声明性解释。但为什么建设性否定没有广泛传播?一个原因可能是这种Prolog解释器的大型设计空间。我注意到我们有以下选择:
向后链接:我们基本上将香草翻译solve/1
吐入两个谓词solvep/1
和solven/1
。 solvep/1
负责解决积极目标,solven/1
负责解决消极目标。当我们尝试这个时,我们迟早会看到我们需要对量词进行更仔细的处理,并最终可能会为Herbrand域提供量化消除方法。
正向链接:我们还会注意到,在反向链接中,我们将遇到问题,即在头部建立具有析取或存在量词的子句。这与适用于Prolog的后续微积分在右侧只有一个公式的事实有关。因此,我们要么在右侧使用多个公式并且会松散不一致,要么我们使用正向链接。
Magic Sets:但是正向链接会以不受控制的方式对解决方案空间进行处理。所以我们可能需要某种形式的前向和后向链接组合。我并不仅仅意味着,我们应该能够在解决过程中在两者之间动态切换,但我的意思是我们还有一种方法可以生成指导正向链接过程的集合。
在这个答案here中也提到了更多问题。这并不意味着迟早会找到一个公式,以便将所有这些问题和解决方案对组合在一起,但可能还需要一些时间。
再见