Вставка вырезки в Пролог, чтобы сделать предложение отношения связанным, но двунаправленным - PullRequest
0 голосов
/ 25 мая 2018

Рассмотрим следующую программу Prolog:

transform([], []).
transform([X | Xs],[Y | Ys]):-
    isSpecial(X),
    isSpecial(Y),
    transformSpecial(X,Y),
    transform(Xs,Ys).
transform([X | Xs],[ X | Ys]):-
    transform(Xs,Ys).

isSpecial(foo).
isSpecial(bar).
isSpecial(foobar).

transformSpecial(X,Y):-
    isSpecial(X),
    isSpecial(Y),
    not(X = Y).

В некоторых случаях я буду звонить tranform([foo, 1, 2], X) и в случаях, когда я хочу позвонить transform(X, [foo, 1, 2]).

В обоих случаях я хочуX объединить с [bar, 1, 2] и [foobar, 1, 2], но не с [foo, 1, 2].То есть я хочу, чтобы, как только предикат правильно распознал, что второе предложение применимо, он должен придерживаться только возврата назад, используя второе предложение.

Куда мне вставить вырезку для достижения такого поведения?

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

Вот еще один подход, который использует ваше существующее определение isSpecial/1.Его самый большой недостаток состоит в том, что он делает много предположений, которые не всегда видны.Так что даже небольшое расширение может сделать его недействительным.Он использует iwhen/2 («экземпляр, когда»).

el_transformed(X, Y) :-
   iwhen(( nonvar(X) ; nonvar(Y) ), iel_transformed(X, Y) ).

iel_transformed(X, Y) :-
   (  nonvar(X)
   -> ( isSpecial(X) -> isSpecial(Y), X \= Y ; X = Y )
   ;  ( isSpecial(Y) -> isSpecial(X), X \= Y ; X = Y )
   ).

Очень сложно - я даже сделал a две ошибки с первой попытки ... иno cut, mince!

Для самого общего запроса теперь мы получаем ошибку инстанцирования.Не очень удовлетворительно, но лучше, чем неправильный результат.

В общем, вам придется искать реализации конструктивного отрицания , чтобы справиться с этим в общем случае ...

0 голосов
/ 25 мая 2018

Ваша программа в настоящее время слишком общая.В конце концов, есть три решения для вашего запроса, но только первое предназначено.Последнее неверно.

?- transform([foo, 1, 2], Xs).
   Xs = [bar, 1, 2]
;  Xs = [foobar, 1, 2]
;  Xs = [foo, 1, 2].   % wrong

Сокращение теперь может уменьшить количество решений.Но в большинстве случаев это идет не так.Ваш вопрос, где вы должны "вставить разрез, чтобы добиться такого поведения?"имеет только в качестве ответа: нет места.Вам нужно сделать гораздо больше для достижения этого.

По сути, то, что вы описываете, является поэлементным преобразованием.Поэтому, может быть, мы придерживаемся описания одного элемента за раз.

el_transformed(X, Y) :-
    isSpecial(X),
    isSpecial(Y),
    dif(X,Y).
el_transformed(X, X).   % too general!

Используйте maplist(el_transformed, [foo, 1, 2], Xs), как и раньше ...

Обратите внимание, что эта версия так же неверна, как и ваш исходный код.Но теперь просто добавить дополнительное условие:

el_transformed(X, Y) :-
   isSpecial(X),
   isSpecial(Y),
   dif(X,Y).
el_transformed(X, X) :-
   dif(X, foo),
   dif(X, bar),
   dif(X, foobar).

Есть один большой недостаток: в некоторых случаях эта версия не очень детерминирована:

?- el_transformed(foobar, bar).
   true
;  false.

Если вы хотитечтобы получить еще больше, рассмотрите возможность использования library(reif), доступных как для SICStus , так и SWI .

el_transformed(X, Y) :-
   if_(( ( X = foo ; X = bar ; X = foobar ),
         ( Y = foo ; Y = bar ; Y = foobar ) ),
       dif(X,Y),
       X = Y).

С этой формулировкой нам не нужно повторятьсами.Чтобы проверить, в порядке ли код, давайте попробуем самый общий запрос:

?- el_transformed(X, Y).
   X = foo, Y = bar
;  X = foo, Y = foobar
;  X = bar, Y = foo
;  X = bar, Y = foobar
;  X = foobar, Y = foo
;  X = foobar, Y = bar
;  X = Y, dif(Y,foobar), dif(Y,bar), dif(Y,foo).

Вы просматриваете все возможные случаи, которые могут произойти!Больше не нужно рассматривать случаи.

Так что, как правило: всякий раз, когда вам нужен предикат, который должен работать "двунаправленно", попробуйте вместо этого написать его как "всенаправленный"!


Возможно, вам не понравилось явное упоминание X = foo ; X = bar ; X = foobar, в этом случае нам пришлось бы копать глубже, увеличив isSpecial/1 до isSpecial_t/2:

isSpecial_t(X, T) :-
   call(
      ( X = foo
      ; X = bar
      ; X = foobar
      ), T).

el_transformed(X, Y) :-
   if_( ( isSpecial_t(X), isSpecial_t(Y) ), dif(X, Y) , X = Y ).
...