Как определить (и назвать) соответствующие предикаты сравнения безопасных терминов в ISO Prolog?

Стандартный порядок терминов (ISO / IEC 13211-1 7.2 Порядок терминов) определяется для всех терминов, включая переменные. Хотя есть хорошие применения для этого представления о реализации setof/3, это делает многие в остальном чистые и логичные использования встроенных в 8.4 Сравнение терминов декларативным кошмаром с бесами (краткая форма для императивных конструкций) повсюду. 8.4 Особенности сравнения терминов:

8.4 Сравнение сроков

8.4.1 (@ = ‹) / 2, (==) / 2, (\ ==) / 2, (@‹) / 2, (@ ›) / 2, (@› =) / 2.
8.4.2 сравнить / 3.
8.4.3 sort / 2.
8.4.4 keysort / 2.

В качестве примера рассмотрим:

?- X @< a.
true.

Это удается, потому что

7.2 Срок действия

Порядок term_precedes (3.181) определяет,
или нет термин X term-предшествует термину Y.

Если X и Y являются идентичными терминами, то оба X term_precedes Y
и Y term_precedes X являются ложными.

Если X и Y имеют разные типы: X термин_представляет Y, если тип
X предшествует типу Y в следующем порядке:
variable предшествует floating point предшествует integer
предшествует atom предшествует compound.

ПРИМЕЧАНИЕ. Встроенные предикаты, которые проверяют порядок терминов
, определены в 8.4.
...

Таким образом, все переменные меньше a. Но как только создается экземпляр X:

?- X @< a, X = a.
X = a.

результат становится недействительным.

Вот в чем проблема. Чтобы преодолеть это, можно либо использовать ограничения, либо придерживаться только основного поведения и, следовательно, создать _ 25_.

7.12.2 Классификация ошибок

Ошибки классифицируются по форме Error_term:

a) Должна быть ошибка создания экземпляра, когда аргумент
или один из его компонентов является переменной, и требуется экземпляр аргумента или компонента
. Он имеет форму instantiation_error.

Таким образом мы точно знаем, что результат хорошо определен, пока не возникает ошибка создания экземпляра.

Для (\==)/2 уже существует dif/2, использующий ограничения, или _ 30_, что приводит к чистой ошибке создания экземпляра.

iso_dif(X, Y) :-
   X \== Y,
   ( X \= Y -> true
   ; throw(error(instantiation_error,iso_dif/2))
   ).

Итак, о чем мой вопрос: как определить (и назвать) соответствующие предикаты сравнения безопасных терминов в Пролог ISO? В идеале, без явного обхода терминов. Возможно, чтобы уточнить: выше iso_dif/2 не используется явный обход термина. И (\==)/2, и (\=)/2 просматривают термин внутри, но накладные расходы для этого чрезвычайно низкие по сравнению с явным обходом с (=..)/2 или functor/3, arg/3.


person false    schedule 03.11.2014    source источник
comment
@mat: Почему бы вам не поместить удаленный ответ как отдельный вопрос? В нем много ценных вопросов.   -  person false    schedule 04.11.2014
comment
Поскольку до сих пор нет ответа: я награжу следующую награду +400, если придет какой-либо хороший ответ.   -  person false    schedule 13.11.2014
comment
Что плохого в использовании freeze/2?   -  person    schedule 06.05.2015
comment
@Boris: Другая проблема. Вам снова придется пройти через условия. В конце концов, обе проблемы должны сначала как-то найти такую ​​критическую пару.   -  person false    schedule 06.05.2015
comment
@Boris: И, freeze/2 сам по себе не будет работать должным образом, вам скорее понадобится when/2 с ?=. Пример: lt(X+2,Y+1), X = Y уже должен выйти из строя.   -  person false    schedule 06.05.2015
comment
Да, подумав более внимательно, я подхожу ближе к тому месту, откуда вы пришли.   -  person    schedule 06.05.2015
comment
Я просто хочу добавить, что, чтобы выразить свою признательность, я буду голосовать за каждый ответ в этой цепочке, который содержит малейшие следы попытки решения, потому что этот вопрос настолько интересен и сложен, и вы много узнаете о терминах, размышляя об этом.   -  person mat    schedule 08.05.2015
comment
Мета-обсуждение, которое связывает этот вопрос в качестве примера: meta.stackoverflow.com / questions / 319598 /   -  person user000001    schedule 23.03.2016


Ответы (8)


iso_dif/2 реализовать намного проще, чем сравнение:

  • Доступен встроенный оператор \=
  • Теперь вы точно, какие аргументы предоставить to\=

Определение

Основываясь на ваших комментариях, безопасное сравнение означает, что порядок не изменится, если будут созданы переменные в обоих подтерминах. Если мы назовем сравнение lt, мы получим, например:

  • lt(a(X), b(Y)): всегда выполняется для всех X и Y, потому что a @< b
  • lt(a(X), a(Y)): точно не знаем: intanciation_error
  • lt(a(X), a(X)): всегда терпит неудачу, потому что X @ ‹X терпит неудачу

Как сказано в комментариях, вы хотите выдать ошибку, если при параллельном обходе обоих терминов первая (потенциально) распознающая пара терминов содержит:

  • две неидентичные переменные (lt(X,Y))
  • переменная и непеременная (lt(X,a) или lt(10,Y))

Но сначала давайте рассмотрим возможные подходы, которые вы не хотите использовать:

  • Определите явную функцию сравнения обхода терминов. Я знал, что вы бы предпочли этого не делать по соображениям производительности, но все же это наиболее простой подход. Я бы рекомендовал сделать это в любом случае, чтобы у вас была эталонная реализация для сравнения с другими подходами.

  • Используйте ограничения для отложенного сравнения: я не знаю, как это сделать с помощью ISO Prolog, но, например, с ECLiPSe, я бы приостановил фактическое сравнение по набору неустановленных переменных (используя term_variables/2) до тех пор, пока не останется больше переменных. Ранее я также предлагал использовать предикат coroutine / 0, но Я упустил из виду тот факт, что он не влияет на оператор @< (только <).

    Этот подход не решает ту же проблему, что вы описываете, но он очень близок. Одно из преимуществ состоит в том, что он не генерирует исключение, если возможные значения, заданные переменным, удовлетворяют сравнению, тогда как lt выдает исключение, если он не знает заранее.

Явный обход термина (эталонная реализация)

Вот реализация подхода явного обхода терминов для lt, безопасной версии @<. Просмотрите его, чтобы убедиться, что вы этого ожидаете. Возможно, я пропустил некоторые дела. Я не уверен, соответствует ли это ISO Prolog, но это тоже можно исправить, если хотите.

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).

(Изменить: с учетом замечаний Тудора Берариу: (i) отсутствует случай ошибки var / var, (ii) сначала упорядочивается по арности; кроме того, исправление (i) позволяет мне удалить subsumes_term для списков. Спасибо.)

Неявный обход терминов (не работает)

Вот моя попытка добиться того же эффекта без деструктуризации.

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 и 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: обратите внимание, что для любого заданного термина, однако, мы можем найти локально максимальный и минимальный термины, которых будет достаточно для цели вопроса.

person coredump    schedule 14.11.2014
comment
Ярлык для одностороннего сопоставления, например pred([A]) :- -?-> A = 1., который не объединяет переменную X при вызове pred(X). Есть ли стандартный способ написать это? - person coredump; 15.11.2014
comment
В ISO есть subsumes_term/2. В моем вопросе есть ссылка на все функции ISO, как встроенные и управляющие конструкции. - person false; 16.11.2014
comment
(корр.) В ECLiPSe coroutine/0 не делает (@<)/2 декларативным: X@<1,X=1 выполняется успешно, а X<1,X=1 - нет. - person false; 16.11.2014
comment
@false Я отредактировал так, чтобы использовать subsumes/2. Кстати, данный код может плохо себя вести с круговыми терминами. Однако обратите внимание, что A=foo(A), B=foo(B), A @< B не завершается, так что, возможно, это не проблема. - person coredump; 16.11.2014
comment
@false Я тестирую (как вы уже догадались) ECLiPSe, так что это может зависеть от реализации. При этом [x] =.. [F|A] объединяет F с . и A с [x,[]]. В результате, если я удалю предложение для списков, предложение с compound/1 никогда не завершится. - person coredump; 16.11.2014
comment
Я рекомендую GNU или SICStus Prolog для соответствия ISO. - person false; 16.11.2014
comment
@false Спасибо, с этого момента я продолжу работу с GNU Prolog. Я тестировал и веду то же самое. - person coredump; 16.11.2014
comment
every(Xs, N) скорее maplist(=(N),Xs) - person false; 16.11.2014
comment
lt(X+a,X+b) должен завершиться успешно, но выдает ошибку - person false; 16.11.2014
comment
Эталонная реализация, выполняющая явный обход терминов, в некоторых случаях неверна, потому что стандартный порядок терминов учитывает арность перед сравнением имен функторов. | ?- lt(b(b), a(a,a)). no против | ?- @<(b(b), a(a,a)). yes. - person Tudor Berariu; 11.01.2015
comment
@ j4nbur53 Я понял вопрос: iso_dif / 2 никогда не должен быть успешным для терминов X и Y, если вы можете найти N и M, которые включают соотв. X и Y, для которых iso_dif / 2 не работает. Это единственное свойство, которое, как утверждается, сохраняется независимо от порядка операций. Ваш пример требует ограничения (если ваш компилятор не работает достаточно усердно), что выходит за рамки этого вопроса. Я что-то пропустил? - person coredump; 24.03.2016

Далее! Это должно работать лучше, чем моя предыдущая попытка:

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)
person repeat    schedule 07.05.2015
comment
@ j4nbur53. Вероятно? Почему вы проголосовали за этот конкретный неправильный ответ и проголосовали против всех моих последующих ответов, которые лучше? Что происходит? - person repeat; 25.03.2016
comment
Это неверный ответ! lt(A+0+B,B+1+A) успешно, хотя безопасное сравнение все еще может качаться в любую сторону: A=3, B=2, lt(A+0+B,B+1+A) не удается, но A=2, B=3, lt(A+0+B,B+1+A) успешно ... Если сопрограммы не используются, создание исключения было бы правильным поведением . - person repeat; 25.03.2016
comment
@ j4nbur53. Предполагаемые подразумеваемые алгебраические свойства не обязательно выполняются в общем случае. Рассмотрим сообщение Матса Карлссона, сравнивающее бесконечные термины с comp.lang.prolog от 16 июля 1996 г .: groups.google.com/d/msg/comp.lang.prolog/Om8bTZ_Mom4/ - person repeat; 25.03.2016
comment
@ j4nbur53. С SICStus Prolog 4.3.2 я пришел (по сути) к такому же выводу ... Остерегайтесь стандартного порядка терминов SWI! Лучше это назвать нестандартным порядком терминов. Цитата из руководства SWI: Переменные ‹ЧислаСтроки ‹Атомы‹ Составные термины. - person repeat; 26.03.2016
comment
@ j4nbur53. Да и нет :) Сначала спросите SWI ?- 1.0 @> 0., а затем рассмотрите следующий отрывок из руководства SICStus Prolog 4.3.2: Переменные по возрасту ([...]). Плавает в числовом порядке (например, -1.0 ставится перед 1.0). Целые числа в числовом порядке (например, -1 ставится перед 1). Атомы в алфавитном (то есть символьном) порядке. Составные термины, упорядоченные сначала по арности, а затем по имени главного функтора, [...]. Мой вывод из этих наблюдений (подтвержденный Яном Вилемакером в частном письме) таков: реализация SWI (@<)/2 и друзья - тупица! - person repeat; 26.03.2016
comment
Но вернемся к nq (X, Y) ‹=› lt (X, Y); lt (Y, X). Стандарт ISO утверждает, что идентичность не подразумевает ни lt (X, Y), ни lt (Y, X), см. Раздел 7.2. Это одно из направлений двусторонней импликации, а именно nq (X, Y) ‹== lt (X, Y); lt (Y, X). Возьмем противопоставление ~ nq (X, Y) = eq (X, Y) == ›~ lt (X, Y) & ~ lt (Y, X). Вопрос в том, требует ли ISO другого направления. - person Mostowski Collapse; 26.03.2016
comment
Другое направление в nq (X, Y) ‹=› lt (X, Y); lt (Y, X) необходим для работы сортировки. Предположим, у нас есть некоторые [A, B] и nq (A, B), и мы хотим их отсортировать. Предположим, что sort задан как sort (X, Y): - permutation (X, Y), sorted (Y). Если у нас нет nq (X, Y) = ›lt (X, Y); lt (Y, X) у нас не будет ни сортировки ([A, B]), ни сортировки ([B, A]). - person Mostowski Collapse; 26.03.2016

Третья попытка! Разработано и протестировано с использованием GNU Prolog 1.4.4.


Приложение «А»: «настолько просто, насколько возможно».

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))
   ).    

Приложение "B": "не нужно маркировать все вары"

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).
person repeat    schedule 09.05.2015
comment
Ваши альфа и омега должны дать evaluation_error(float_overflow), если у вас нет очень продвинутого представления с плавающей запятой. - person false; 11.05.2015
comment
Я не очень доволен, но вы соответствуете критериям. Я надеялся, что это можно будет сделать намного быстрее ... - person false; 11.05.2015
comment
@ложный. У меня нет ошибок переполнения, разве это не должно давать мне FP минус бесконечность? - person repeat; 11.05.2015
comment
В ISO Prolog нет такого значения - пожалуйста, обратитесь к некоторым обсуждениям, связанным с float, которые мы провели в декабре / январе. - person false; 11.05.2015
comment
Голосование вниз, поскольку label_vars / 3 порождает значительные накладные расходы, вопреки требованиям OP. Почему за это вообще должно быть вознаграждение? - person Mostowski Collapse; 24.03.2016
comment
@ j4nbur53. IIRC этот ответ был первым, который прошел все тестовые примеры, указанные OP в вопросе и другими людьми в их ответах и ​​комментариях. Конечно, код очень неэффективен ... Я бы хотел увидеть альтернативу, не использующую явный обход терминов (и дающую те же ответы)! - person repeat; 24.03.2016

Это не полностью оригинальный ответ, поскольку он основан на ответе @ coredump.

Есть один тип запросов lt/2 (эталонная реализация, выполняющая явный обход терминов) не дает правильного ответа:

| ?- lt(b(b), a(a,a)).

no
| ?- @<(b(b), a(a,a)).

yes

Причина в том, что в стандартном порядке терминов перед сравнением имен функторов учитывается арность.

Во-вторых, 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 для всех предыдущих пар Xj, Yj (например, lt_args([a,X,a(X),b|_], [a,X,a(X),c|_]) истинно).

Некоторые примеры запросов:

| ?- lt(a(X,Y,c(c),_Z1), a(X,Y,b(b,b),_Z2)).

yes
| ?- lt(a(X,_Y1,c(c),_Z1), a(X,_Y2,b(b,b),_Z2)).
uncaught exception: error(instatiation_error)
person Tudor Berariu    schedule 11.01.2015
comment
... почему нет случая для [] в lt_args/2? Может иметь смысл, но это немного удивительно - person false; 11.01.2015
comment
Это означало бы, что все аргументы эквивалентны. - person Tudor Berariu; 11.01.2015

В этом ответе мы представляем предикат safe_term_less_than/2, монотонный аналог iso-prolog встроенный предикат (@<)/2 (§8.4 .1, «срок меньше»). Его основные свойства:

  • Явный обход рекурсивных терминов.
  • На основе возможностей prolog-coroutining, в частности, when/2.

    • Сравнение может прогрессировать постепенно:

      • "freeze" whenever instantiation is not sufficient
      • "просыпаться" всякий раз, когда изменяется реализация наиболее значимых условий
    • Текущая передняя линия сравнения представлена ​​в виде явного стека (LIFO).

    • Текущее состояние напрямую передается остаточным целям.

Следующий код был разработан и протестирован на sicstus- пролог версии 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. терпит неудачу - как и должно быть!
person repeat    schedule 01.12.2015
comment
@ложный. Я еще не совсем уверен, может ли when((?=(L,R) ; nonvar(L),nonvar(R)),...) когда-нибудь потребоваться повторное замораживание ... Если ?=(L,R) успешно, подразумевается ли (L == R ; nonvar(L),nonvar(R))? - person repeat; 02.12.2015
comment
Вышеупомянутое не суммирует числа, как это делает SWI-Prolog, и, вероятно, также не будет обрабатывать рациональные деревья, или бесконечные термины. Разве это не указывает на то, что одно решение для обхода не может охватывать все системы Prolog. - person Mostowski Collapse; 26.03.2016
comment
С другой стороны, мы до сих пор не знаем, является ли идея iso_dif / 2, которая подразумевает принятие желаемого за действительное, что она работает для множества систем Пролога, поскольку (==) / 2 и (\ =) / 2 будут отражать возможна или невозможна специфика различных систем. Но алгебраические свойства уже могли опровергнуть некоторые из утверждений. - person Mostowski Collapse; 26.03.2016

Какого черта! Я тоже попробую!

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_alpha и T_omega, не двух свежих переменных.

  • lt(X,Y) делает две копии X (X1 и X2) и две копии Y (Y1 и Y2).
  • Общие переменные X и Y также используются X1 и Y1, а также X2 и Y2.
  • T_alpha стоит раньше всех остальных терминов (в X1, X2, Y1, Y2) относительно стандартный заказ.
  • T_omega идет после всех остальных терминов в стандартном порядке.
  • In the copied terms, the variables that are in X but not in Y (and vice versa) are unified with T_alpha / T_omega.
    • If this has an impact on term ordering, we cannot yet decide the ordering.
    • Если это не, все готово.

Теперь контрпример @false работает:

?- lt(X+1,1+2).
uncaught exception: error(instantiation_error,lt/2)
?- X=2, lt(X+1,1+2).
no
person repeat    schedule 05.05.2015
comment
Контрпример: lt(X+1,1+2) успешно, но X = 2, ... терпит неудачу! - person false; 05.05.2015
comment
Я думал о некоторых T, которые идут после всех остальных терминов с упорядочением терминов (не перед ними). - person repeat; 05.05.2015
comment
functor(F,z,255) должно быть достаточно для многих случаев - в действительности, не для всех. - person false; 05.05.2015
comment
Нет необходимости в тестировании методом перебора: вы предполагаете, что релевантными являются только те переменные, которые встречаются на одной стороне. lt(X+X+2,X+1+3). Должна быть ошибка, но это не так. - person false; 07.05.2015

Вот набросок того, что, по моему мнению, могло бы быть рабочим подходом. Рассмотрим цель lt(X, Y) и term_variables(X, XVars), term_variables(Y, YVars).

Цель определения - определить, может ли дальнейшее создание экземпляра изменить порядок терминов (7.2). Поэтому мы могли бы захотеть напрямую выяснить ответственные переменные. Поскольку term_variables/2 проходит через термин точно так же, как это имеет отношение к порядку терминов, выполняется следующее:

Если есть экземпляр, который изменяет порядок терминов, то переменные, которые должны быть созданы, чтобы засвидетельствовать это изменение, находятся в префиксах списка XCs, YCs из XVars и YVars соответственно, и либо

  1. XCs, YCs, XVars и YVars идентичны, или

  2. XCs и YCs идентичны до последнего элемента, или

  3. XCs и YCs идентичны до конца, где один список имеет дополнительный элемент, а другой список идентичен соответствующему списку переменных XVars или YVars.

В качестве интересного особого случая, если первые элементы в XVars и YVars различаются, то это единственные переменные, которые нужно проверить на релевантность. Таким образом, это включает случай, когда нет общей переменной, но он даже более общий, чем это.

person false    schedule 07.05.2015

Этот ответ является продолжением моего предыдущего ответа, в котором был представлен 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 такие же, как , определенные ранее.

person repeat    schedule 02.12.2015