Unification with STO detection

前端 未结 5 1537
野的像风
野的像风 2021-02-06 22:26

In ISO Prolog unification is defined only for those cases that are NSTO (not subject to occurs-check). The idea behind is to cover those cases of unifications that are mostly u

5条回答
  •  傲寒
    傲寒 (楼主)
    2021-02-06 22:48

    Here goes another attempt:

    unify_sto(X,Y):-
      unify_with_occurs_check(X,Y) -> true ;
      (
        term_general(X, Y, XG, YG),
        \+(unify_sto1(XG,YG)),
        throw(error(type_error(acyclic,unify(X,Y)),_))
      ).
    
    unify_sto1(X, Y):-
       unify_with_occurs_check(X,Y).
    unify_sto1(X, Y):-
       X\=Y.
    
    term_general(X, Y, XG, YG):-
      ((var(X) ; var(Y)) ->  (XG=X, YG=Y) ;
      ((
        functor(X, Functor, Len),
        functor(Y, Functor, Len),
        X=..[_|XL],
        Y=..[_|YL],
        term_general1(XL, YL, NXL, NYL)
      ) ->
      (
        XG=..[Functor|NXL],
        YG=..[Functor|NYL]
      ) ;
      ( XG=_, YG=_ )
      )).
    
    term_general1([X|XTail], [Y|YTail], [XG|XGTail], [YG|YGTail]):-
      term_general(X, Y, XG, YG),
      term_general1(XTail, YTail, XGTail, YGTail).
    term_general1([], [], [], []).
    

    It first tries to unify_with_occurs_check, and if it does not succeed then it proceed to build two more general terms, traversing the structure of each term.

    • If either term is a variable it leaves both terms them as-is.
    • If both terms are the same atom, or if they are both compund terms with the same functor and arity [*], it traverses its arguments making a more general term for them.
    • Otherwise it assigns a fresh new variable to each term.

    Then it tries again to unify_with_occurs_check the more general terms to test for acyclic unify and throw an error accordingly.

    [*] The test for arity in compund terms is done greedily, as term_general1/4 will fail recursion as OP stated to only use builtin predicates defined in part 1 of this link with does not include length/2.. (edited: Added two functor/3 calls to test for functor and arity before calling term_general1, so as to not try inspect inside terms if their arity does not match)

    E.g:

    ?- unify_sto(s(1)+A,A+s(B)).
    A = s(1),
    B = 1
    ?- unify_sto(1+A,2+s(A)).
    ERROR: Type error: `acyclic' expected, found `unify(1+_G5322,2+s(_G5322))'
    ?- unify_sto(a(1)+X,b(1)+s(X)).
    ERROR: Type error: `acyclic' expected, found `unify(a(1)+_G7068,b(1)+s(_G7068))'
    

    Edit 06/02/2015:

    The solution above fails for the query:

    unify_sto(A+A,a(A)+b(A)).
    

    is it does not yield a unify error.

    Here goes an improvement over the algorithm that deals with each subterm pairwise and yields the error as soon as it discovers it:

    unify_sto(X,Y):-
      unify_with_occurs_check(X,Y) -> true ;
      (
       term_general(X, Y, unify(X,Y), XG, YG),
       \+unify_with_occurs_check(XG,YG),
       throw(error(type_error(acyclic,unify(X,Y)),_))
      ).
    
    unify_sto1(X, Y):-
       unify_with_occurs_check(X,Y).
    unify_sto1(X, Y):-
       X\=Y.
    
    term_general(X, Y, UnifyTerm, XG, YG):-
      ((var(X) ; var(Y)) ->  (XG=X, YG=Y) ;
      ((
        functor(X, Functor, Len),
        functor(Y, Functor, Len),
        X=..[Functor|XL],
        Y=..[Functor|YL],
        term_general1(XL, YL, UnifyTerm, NXL, NYL)
      ) ->
      (
        XG=..[Functor|NXL],
        YG=..[Functor|NYL]
      ) ;
      ( XG=_, YG=_ )
      )).
    
    term_general1([X|XTail], [Y|YTail], UnifyTerm, [XG|XGTail], [YG|YGTail]):-
      term_general(X, Y, UnifyTerm, XG, YG),
      \+(unify_with_occurs_check(XG,YG))-> throw(error(type_error(acyclic,UnifyTerm),_)) ;
      term_general1(XTail, YTail, UnifyTerm, XGTail, YGTail).
    term_general1([], [], _, [], []).
    

    Test case for the query which yielded wrong results in the original answer:

       ?-  unify_sto(A+A,a(A)+b(A)).
        ERROR: Type error: `acyclic' expected, found `unify(_G6902+_G6902,a(_G6902)+b(_G6902))'
       ?- unify_sto(A+A, a(_)+b(A)).
        ERROR: Type error: `acyclic' expected, found `unify(_G5167+_G5167,a(_G5173)+b(_G5167))'
    

提交回复
热议问题