如何在ISO Prolog中定义(和命名)相应的安全术语比较谓词?

时间:2014-11-03 18:33:07

标签: sorting prolog iso-prolog prolog-dif

标准期限订单(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之前。

     

如果XY是相同的字词,那么X term_precedes Y
  Y term_precedes X都是假的。

     

如果XY的类型不同:X term_precedes Y 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}}的显式遍历相比,此处的开销极低。

8 个答案:

答案 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成功。 我们想检查关系是否依赖于一些未初始化的变量。 因此,我会生成Y2X的{​​{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处理XY中出现的变量:

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,这是 built-in predicate (@<)/2 (§8.4.1,“小于”的术语)的单调模拟。它的主要特性是:

  • 显式遍历递归术语。
  • 基于设施,特别是when/2

    • 比较可能会逐步

      • 每当实例化不充分时“冻结”
      • 每当最重要的术语实例化发生变化时
      • “醒来”
    • 比较的当前前线表示为显式(LIFO)堆栈。

    • 目前的状态直接围绕剩余目标传递。

以下代码已在版本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

编辑2015-05-06

lt/2的实施更改为使用T_alphaT_omega而不是两个新变量

  • lt(X,Y)制作了两份XX1X2)和两份YY1和{{1} })。
  • Y2X的共享变量也由YX1以及Y1X2共享。
  • Y2之前所有其他字词(T_alphaX1X2Y1)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}},YCsXVars的列表前缀中分别为{1}}和

  1. YVarsXCsYCsXVars相同,或

  2. YVarsXCs在最后一个元素上完全相同,或者

  3. YCsXCs完全相同,其中一个列表具有另一个元素,另一个列表与其对应的变量列表YCs或{{ 1}}。

  4. 作为一个有趣的特例,如果XVarsYVars中的第一个元素不同,那么这些元素是唯一要测试相关性的变量。所以这个包括没有公共变量的情况,但它甚至更普遍。

答案 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/2args_args_paired//2defined previously相同。