标准期限订单(ISO / IEC 13211-1 7.2期限订单)是在所有条款(包括变量)上定义的。虽然有很好的用途 - 想想setof/3
的实现,但这使得8.4中的内置项的许多其他清洁和逻辑用法比较了一个带有imps的声明性噩梦(命令式构造的简短形式) 。 8.4术语比较功能:
8.4术语比较
8.4.1(@ =<)/ 2,(==)/ 2,(\ ==)/ 2,(@<)/ 2,(@>)/ 2, (@> =)/ 2。
8.4.2 compare/3。
8.4.3 sort/2。
8.4.4 keysort/2
举个例子,考虑一下:
?- X @< a.
true.
这成功了,因为
7.2期限订单
订购 term_precedes (3.181)定义是否或 不是术语
X
术语 - 在术语Y
之前。如果
X
和Y
是相同的字词,那么X
term_precedesY
Y
term_precedesX
都是假的。如果
X
和Y
的类型不同:X
term_precedesY
iff the
X
的类型按以下顺序排在Y
类型之前:
variable
floating point
位于integer
之前atom
在compound
之前a
之前。注 - 内置谓词,用于测试术语的排序
在8.4中定义 ...
因此所有变量都小于X
。但是一旦?- X @< a, X = a.
X = a.
被实例化:
Error_term
结果无效。
这就是问题所在。为了克服这个问题,可以使用约束,或者仅仅坚持核心行为,因此产生instantiation_error
。
7.12.2错误分类
错误根据
的形式分类instantiation_error
:a)当有一个实例化错误时 参数或其中一个组件是一个变量,和一个 实例化的参数或组件是必需的。它有 表格
(\==)/2
。
通过这种方式,我们确信只要没有发生实例化错误就可以很好地定义结果。
对于iso_dif(X, Y) :-
X \== Y,
( X \= Y -> true
; throw(error(instantiation_error,iso_dif/2))
).
,已经有dif/2
使用约束或iso_dif/2
会产生干净的实例化错误。
iso_dif/2
那么我的问题是:如何在ISO Prolog中定义(和命名)相应的安全术语比较谓词?理想情况下,没有任何明确的术语遍历。也许澄清一下:上面(\==)/2
不使用任何明确的术语遍历。 (\=)/2
和(=..)/2
都在内部遍历该术语,但与使用functor/3, arg/3
或{{1}}的显式遍历相比,此处的开销极低。
答案 0 :(得分:7)
iso_dif/2
比比较更容易实现:
\=
运算符\=
根据您的评论,安全比较意味着如果两个子项中的变量都是实例化的,则订单不会发生变化。如果我们将比较命名为lt
,我们就会举例说明:
lt(a(X), b(Y))
:始终适用于所有X和Y,因为a @< b
lt(a(X), a(Y))
:我们不确定:intanciation_error
lt(a(X), a(X))
:总是失败,因为X @&lt; X失败如评论中所述,如果在并排遍历这两个术语时,第一个(可能)有区别的术语对包含:
,则需要抛出错误lt(X,Y)
)lt(X,a)
或lt(10,Y)
)但首先,让我们回顾一下您不想使用的可能方法:
定义明确的术语 - 遍历比较函数。出于性能原因,我知道你并不愿意,但这仍然是最简单的方法。无论如何,我建议你这样做,以便你有一个参考实现来与其他方法进行比较。
使用约束进行延迟比较:我不知道如何使用ISO Prolog进行比较,但是使用例如ECLiPSe,我会暂停对未经证实的变量集合的实际比较(使用term_variables/2
),直到没有更多变量为止。以前,我还建议使用coroutine/0谓词,但我忽略了它不会影响@<
运算符(仅<
)的事实。
这种方法并没有解决与您描述的完全相同的问题,但它非常接近。一个优点是,如果赋予变量的最终值满足比较,则不会抛出异常,而lt
会在事先不知道的情况下抛出异常。
以下是lt
的明确术语遍历方法的实现,@<
是lt(X,Y) :- X == Y,!,
fail.
lt(X,Y) :- (var(X);var(Y)),!,
throw(error(instanciation_error)).
lt(X,Y) :- atomic(X),atomic(Y),!,
X @< Y.
lt([XH|XT],[YH|YT]) :- !,
(XH == YH ->
lt(XT,YT)
; lt(XH,YH)).
lt(X,Y) :-
functor(X,_,XA),
functor(Y,_,YA),
(XA == YA ->
X =.. XL,
Y =.. YL,
lt(XL,YL)
; XA < YA).
的安全版本。
请检查它以检查这是否是您所期望的。我可能错过了一些案例。我不确定这是否符合ISO Prolog,但如果你愿意,也可以修复。
subsumes_term
(编辑:考虑到Tudor Berariu的评论:(i)缺少var / var错误案例,(ii)首先按arity排序;此外,fix(i)允许我删除every([],_).
every([X|L],X) :-
every(L,X).
lt(X,Y) :-
copy_term(X,X2),
copy_term(Y,Y2),
term_variables(X2,VX),
term_variables(Y2,VY),
every(VX,1),
every(VY,0),
(X @< Y ->
(X2 @< Y2 ->
true
; throw(error(instanciation_error)))
; (X2 @< Y2 ->
throw(error(instanciation_error))
; false)).
对于列表。谢谢。)
这是我试图在没有解构条款的情况下达到同样的效果。
X @< Y
假设X2
成功。
我们想检查关系是否依赖于一些未初始化的变量。
因此,我会生成Y2
和X
的{{1}}和Y
的相应副本,其中所有变量都是实例化的:
X2
中,变量与1统一。Y2
中,变量与0统一。因此,如果关系X2 @< Y2
仍然存在,我们知道我们不依赖于变量之间的标准术语排序。否则,我们抛出异常,因为这意味着之前未发生的1 @< 0
关系使关系失败。
(基于OP的评论)
lt(X+a,X+b)
应该会成功,但会产生错误。
乍一看,人们可能会认为统一两个术语中具有相同值的变量,比如val
,可以解决这种情况。但是,在比较的条款中可能会出现X
的其他情况,这会导致错误的判断。
lt(X,3)
应该会产生错误,但会成功。
为了解决这种情况,我们应该将X
与大于3的内容统一起来。在一般情况下,X
应该采用大于其他任何可能的值术语 的 1 强> 。除了实际限制之外,@<
关系没有最大值:复合项大于非复合项,根据定义,复合项可以非常精细。
所以,这种方法并不是决定性的,我认为不能轻易纠正。
1 :请注意,对于任何给定的术语,我们都可以找到本地最大和最小的术语,这对于问题的目的就足够了。
答案 1 :(得分:5)
下一步!这应该比我的previous attempt更好:
lt(X,Y) :-
X \== Y,
( X \= Y
-> term_variables(X,Xvars),
term_variables(Y,Yvars),
T_alpha is -(10.0^1000), % HACK!
functor(T_omega,z,255), % HACK!
copy_term(t(X,Y,Xvars,Yvars),t(X1,Y1,X1vars,Y1vars)),
copy_term(t(X,Y,Xvars,Yvars),t(X2,Y2,X2vars,Y2vars)),
copy_term(t(X,Y,Xvars,Yvars),t(X3,Y3,X3vars,Y3vars)),
copy_term(t(X,Y,Xvars,Yvars),t(X4,Y4,X4vars,Y4vars)),
maplist(=(T_alpha),X1vars), maplist(maybe_unify(T_omega),Y1vars),
maplist(=(T_omega),X2vars), maplist(maybe_unify(T_alpha),Y2vars),
maplist(=(T_omega),Y3vars), maplist(maybe_unify(T_alpha),X3vars),
maplist(=(T_alpha),Y4vars), maplist(maybe_unify(T_omega),X4vars),
% do T_alpha and T_omega have an impact on the order?
( compare(Cmp,X1,Y1),
compare(Cmp,X2,Y2),
compare(Cmp,X3,Y3),
compare(Cmp,X4,Y4),
-> Cmp = (<) % no: demand that X @< Y holds
; throw(error(instantiation_error,lt/2))
)
; throw(error(instantiation_error,lt/2))
).
辅助maybe_unify/2
处理X
和Y
中出现的变量:
maybe_unify(K,X) :-
( var(X)
-> X = K
; true
).
使用GNU-Prolog 1.4.4进行检查:
?- lt(a(X,Y,c(c),Z1), a(X,Y,b(b,b),Z2)).
yes
?- lt(a(X,Y,b(b,b),Z1), a(X,Y,c(c),Z2)).
no
?- lt(a(X,Y1,c(c),Z1), a(X,Y2,b(b,b),Z2)).
uncaught exception: error(instantiation_error,lt/2)
?- lt(a(X,Y1,b(b,b),Z1), a(X,Y2,c(c),Z2)).
uncaught exception: error(instantiation_error,lt/2)
?- lt(b(b), a(a,a)).
yes
?- lt(a(X), a(Y)).
uncaught exception: error(instantiation_error,lt/2)
?- lt(X, 3).
uncaught exception: error(instantiation_error,lt/2)
?- lt(X+a, X+b).
yes
?- lt(X+a, Y+b).
uncaught exception: error(instantiation_error,lt/2)
?- lt(a(X), b(Y)).
yes
?- lt(a(X), a(Y)).
uncaught exception: error(instantiation_error,lt/2)
?- lt(a(X), a(X)).
no
?- lt(X+1,1+2).
uncaught exception: error(instantiation_error,lt/2)
?- lt(X+X+2,X+1+3). % NEW
uncaught exception: error(instantiation_error,lt/2)
答案 2 :(得分:5)
第三次尝试!使用GNU Prolog 1.4.4进行开发和测试。
展览&#39; A&#39;:&#34;尽可能简单&#34;
lt(X,Y) :-
X \== Y,
( X \= Y
-> alpha_omega(Alpha,Omega),
term_variables(X+Y,Vars), % A
\+ \+ (label_vars(Vars,Alpha,Omega), X @< Y),
( \+ (label_vars(Vars,Alpha,Omega), X @> Y)
-> true
; throw(error(instantiation_error,lt/2))
)
; throw(error(instantiation_error,lt/2))
).
展览&#39; B&#39;:&#34; no need to label all vars&#34;
lt(X,Y) :-
X \== Y,
( X \= Y
-> alpha_omega(Alpha,Omega),
term_variables(X,Xvars), % B
term_variables(Y,Yvars), % B
vars_vars_needed(Xvars,Yvars,Vars), % B
\+ \+ (label_vars(Vars,Alpha,Omega), X @< Y),
( \+ (label_vars(Vars,Alpha,Omega), X @> Y)
-> true
; throw(error(instantiation_error,lt/2))
)
; throw(error(instantiation_error,lt/2))
).
vars_vars_needed([], [], []).
vars_vars_needed([A|_], [], [A]).
vars_vars_needed([], [B|_], [B]).
vars_vars_needed([A|As],[B|Bs],[A|ABs]) :-
( A \== B
-> ABs = [B]
; vars_vars_needed(As,Bs,ABs)
).
一些共享代码:
alpha_omega(Alpha,Omega) :- Alpha is -(10.0^1000), % HACK! functor(Omega,z,255). % HACK! label_vars([],_,_). label_vars([Alpha|Vs],Alpha,Omega) :- label_vars(Vs,Alpha,Omega). label_vars([Omega|Vs],Alpha,Omega) :- label_vars(Vs,Alpha,Omega).
答案 3 :(得分:4)
这不是一个完全原创的答案,因为它建立在@ coredump&#39; s answer之上。
有一种类型的查询lt/2
(执行显式术语遍历的参考实现)无法正确回答:
| ?- lt(b(b), a(a,a)).
no
| ?- @<(b(b), a(a,a)).
yes
原因是术语的标准顺序在比较函子名称之前会考虑arity。
其次,在比较变量时,lt/2
并不总是抛出instatiation_error:
| ?- lt(a(X), a(Y)).
no
我在这里写了另一个参考显式实现的候选人:
lt(X,Y):- var(X), nonvar(Y), !, throw(error(instantiation_error)).
lt(X,Y):- nonvar(X), var(Y), !, throw(error(instantiation_error)).
lt(X,Y):-
var(X),
var(Y),
( X \== Y -> throw(error(instatiation_error)) ; !, false).
lt(X,Y):-
functor(X, XFunc, XArity),
functor(Y, YFunc, YArity),
(
XArity < YArity, !
;
(
XArity == YArity, !,
(
XFunc @< YFunc, !
;
XFunc == YFunc,
X =.. [_|XArgs],
Y =.. [_|YArgs],
lt_args(XArgs, YArgs)
)
)
).
lt_args([X1|OtherX], [Y1|OtherY]):-
(
lt(X1, Y1), !
;
X1 == Y1,
lt_args(OtherX, OtherY)
).
当存在一对相应的参数lt_args(Xs, Ys)
,Xi
时,谓词Yi
为真,lt(Xi, Yi)
和Xj == Yj
为所有先前的对{ {1}},Xj
(例如Yj
为真)。
一些示例查询:
lt_args([a,X,a(X),b|_], [a,X,a(X),c|_])
答案 4 :(得分:4)
在这个答案中,我们提出谓词safe_term_less_than/2
,这是iso-prolog built-in predicate (@<)/2
(§8.4.1,“小于”的术语)的单调模拟。它的主要特性是:
基于prolog-coroutining设施,特别是when/2
。
比较可能会逐步 :
比较的当前前线表示为显式(LIFO)堆栈。
以下代码已在sicstus-prolog版本4.3.2:
上开发和测试safe_term_less_than(L, R) :- % exported predicate
i_less_than_([L-R]).
safe_term_less_than/2
的上述定义基于以下辅助谓词:
i_less_than_([L-R|LRs]) :- Cond = (?=(L,R) ; nonvar(L),nonvar(R)), when(Cond, i_lt_step_(L,R,LRs)). i_lt_step_(L, R, LRs) :- ( L == R -> i_less_than_(LRs) ; term_itype(L, L_type), term_itype(R, R_type), compare(Ord, L_type, R_type), ord_lt_step_(Ord, L, R, LRs) ). term_itype(V, T) :- ( var(V) -> throw(error(instantiation_error,_)) ; float(V) -> T = t1_float(V) ; integer(V) -> T = t2_integer(V) ; callable(V) -> T = t3_callable(A,F), functor(V, F, A) ; throw(error(system_error,_)) ). ord_lt_step_(<, _, _, _). ord_lt_step_(=, L, R, LRs) :- ( compound(L) -> L =.. [_|Ls], R =.. [_|Rs], phrase(args_args_paired(Ls,Rs), LRs0, LRs), i_less_than_(LRs0) ; i_less_than_(LRs) ). args_args_paired([], []) --> []. args_args_paired([L|Ls], [R|Rs]) --> [L-R], args_args_paired(Ls, Rs).
示例查询:
| ?- safe_term_less_than(X, 3). prolog:trig_nondif(X,3,_A,_B), prolog:trig_or([_B,X],_A,_A), prolog:when(_A,(?=(X,3);nonvar(X),nonvar(3)),user:i_lt_step_(X,3,[])) ? yes | ?- safe_term_less_than(X, 3), X = 4. no | ?- safe_term_less_than(X, 3), X = 2. X = 2 ? ; no | ?- safe_term_less_than(X, a). prolog:trig_nondif(X,a,_A,_B), prolog:trig_or([_B,X],_A,_A), prolog:when(_A,(?=(X,a);nonvar(X),nonvar(a)),user:i_lt_step_(X,a,[])) ? ; no | ?- safe_term_less_than(X, a), X = a. no | ?- safe_term_less_than(X+2, Y+1), X = Y. no
与之前的答案相比,我们观察到:
?- safe_term_less_than(X+2, Y+1), X = Y.
失败 - 就像它应该的那样!答案 5 :(得分:3)
哎呀!我也试一试!
lt(X,Y) :-
X \== Y,
( X \= Y
-> term_variables(X,Xvars),
term_variables(Y,Yvars),
list_vars_excluded(Xvars,Yvars,XonlyVars),
list_vars_excluded(Yvars,Xvars,YonlyVars),
_ = s(T_alpha),
functor(T_omega,zzzzzzzz,255), % HACK!
copy_term(t(X,Y,XonlyVars,YonlyVars),t(X1,Y1,X1onlyVars,Y1onlyVars)),
copy_term(t(X,Y,XonlyVars,YonlyVars),t(X2,Y2,X2onlyVars,Y2onlyVars)),
maplist(=(T_alpha),X1onlyVars), maplist(=(T_omega),Y1onlyVars),
maplist(=(T_omega),X2onlyVars), maplist(=(T_alpha),Y2onlyVars),
% do T_alpha and T_omega have an impact on the order?
( compare(Cmp,X1,Y1),
compare(Cmp,X2,Y2)
-> Cmp = (<) % no: demand that X @< Y holds
; throw(error(instantiation_error,lt/2))
)
; throw(error(instantiation_error,lt/2))
).
更辅助的东西:
listHasMember_identicalTo([X|Xs],Y) :-
( X == Y
-> true
; listHasMember_identicalTo(Xs,Y)
).
list_vars_excluded([],_,[]).
list_vars_excluded([X|Xs],Vs,Zs) :-
( listHasMember_identicalTo(Vs,X)
-> Zs = Zs0
; Zs = [X|Zs0]
),
list_vars_excluded(Xs,Vs,Zs0).
让我们进行一些测试(使用GNU Prolog 1.4.4):
?- lt(a(X,Y,c(c),Z1), a(X,Y,b(b,b),Z2)). yes ?- lt(a(X,Y,b(b,b),Z1), a(X,Y,c(c),Z2)). no ?- lt(a(X,Y1,c(c),Z1), a(X,Y2,b(b,b),Z2)). uncaught exception: error(instantiation_error,lt/2) ?- lt(a(X,Y1,b(b,b),Z1), a(X,Y2,c(c),Z2)). uncaught exception: error(instantiation_error,lt/2) ?- lt(b(b), a(a,a)). yes ?- lt(a(X), a(Y)). uncaught exception: error(instantiation_error,lt/2) ?- lt(X, 3). uncaught exception: error(instantiation_error,lt/2) ?- lt(X+a, X+b). yes ?- lt(X+a, Y+b). uncaught exception: error(instantiation_error,lt/2) ?- lt(a(X), b(Y)). yes ?- lt(a(X), a(Y)). uncaught exception: error(instantiation_error,lt/2) ?- lt(a(X), a(X)). no
将lt/2
的实施更改为使用T_alpha
和T_omega
,而不是两个新变量。
lt(X,Y)
制作了两份X
(X1
和X2
)和两份Y
(Y1
和{{1} })。Y2
和X
的共享变量也由Y
和X1
以及Y1
和X2
共享。Y2
来之前所有其他字词(T_alpha
,X1
,X2
,Y1
)w.r.t。标准订单。Y2
在标准订单中的所有其他条款之后。T_omega
但不在X
中的变量(反之亦然)与Y
/ T_alpha
统一。
现在,@ false给出的反例是:
T_omega
答案 6 :(得分:3)
这是我认为可能是一种工作方法的草图。考虑目标lt(X, Y)
和term_variables(X, XVars), term_variables(Y, YVars)
。
定义的目的是确定进一步的实例化是否可能改变术语顺序(7.2)。所以我们可能想直接找出负责任的变量。由于term_variables/2
以与术语顺序相关的方式遍历一个术语,因此以下内容适用:
如果存在更改术语顺序的实例化,那么必须实例化以见证该更改的变量位于XCs
的{{1}},YCs
和XVars
的列表前缀中分别为{1}}和
YVars
,XCs
,YCs
和XVars
相同,或
YVars
和XCs
在最后一个元素上完全相同,或者
YCs
和XCs
完全相同,其中一个列表具有另一个元素,另一个列表与其对应的变量列表YCs
或{{ 1}}。
作为一个有趣的特例,如果XVars
和YVars
中的第一个元素不同,那么这些元素是唯一要测试相关性的变量。所以这个包括没有公共变量的情况,但它甚至更普遍。
答案 7 :(得分:3)
我的previous one提供了safe_term_less_than/2
。
下一步是什么? compare/3
的安全变体 - 缺乏想象力的scompare/3
:
scompare(Ord, L, R) :- i_scompare_ord([L-R], Ord). i_scompare_ord([], =). i_scompare_ord([L-R|Ps], X) :- when((?=(L,R);nonvar(L),nonvar(R)), i_one_step_scompare_ord(L,R,Ps,X)). i_one_step_scompare_ord(L, R, LRs, Ord) :- ( L == R -> scompare_ord(LRs, Ord) ; term_itype(L, L_type), term_itype(R, R_type), compare(Rel, L_type, R_type), ( Rel \== (=) -> Ord = Rel ; compound(L) -> L =.. [_|Ls], R =.. [_|Rs], phrase(args_args_paired(Ls,Rs), LRs0, LRs), i_scompare_ord(LRs0, Ord) ; i_scompare_ord(LRs , Ord) ) ).
谓词term_itype/2
和args_args_paired//2
与defined previously相同。