Дизъюнкция G1; G2 против If-then-else Cond - ›G1; G2

Я столкнулся с программой на Прологе, содержащей вложенное if-then-else вида

p(X,Y) :-
     (cond1(X,Y) -> q(X)); true,
     (cond2(X,Y) -> q(Y)); true.

у которого были неожиданные ответы. Причина такого поведения та же, что и в следующих запросах:

?- (true, (true -> X=a)); X=b.
X = a ;
X = b.

?- ((true -> X=a), true); X=b.
X = a ;
X = b.

?- (true -> X=a); X=b.
X = a.

На первый запрос есть два ответа, а на второй - только один. В чем причина различного поведения?

P.S .: Я знаю разницу, но я не нашел ТАК-вопрос, который касался бы этого довольно запутанного явления. Так почему бы не задокументировать это таким образом?


person lambda.xy.x    schedule 06.06.2019    source источник
comment
Интересное поведение: ?- (true -> X=a); X=b. я получаю X = a. Вместо этого: trace, (true -> X=a); X=b. я получаю X = a ; X = b. Я в сети SWISH.   -  person damianodamiano    schedule 07.06.2019
comment
Это очень интересно. Я всегда считал p1 -> p2 эквивалентом p1, !, p2. Однако (true, !, X=a), true); X = b дает только одно решение X=a, тогда как ((true -> X=a), true); X=b. дает два. Я действительно удивлен, что это дает два.   -  person lurker    schedule 07.06.2019
comment
@damianodamiano интересно. Просто в gprolog trace, (true -> X=a); X=b. дает два решения, но trace, (true, !, X=a) ; X=b дает только одно (X=a), чего я ожидал и от ->.   -  person lurker    schedule 07.06.2019
comment
Guy Coder: Я хотел, чтобы другие люди заработали очки и добавили мое решение в конце. Я могу вставить его сейчас, если хочешь.   -  person lambda.xy.x    schedule 07.06.2019
comment
См. Ту же проблему здесь: stackoverflow.com/q/56111688/3768871   -  person OmG    schedule 07.06.2019
comment
@OmG: этот ответ касается ограничений и «если-то-еще». Этот эффект представляет собой простой пролог о различии между дизъюнкцией ; и if-then-else -> ;.   -  person lambda.xy.x    schedule 07.06.2019
comment
I wanted other people to earn the points and add my solution in the end. Я тоже так думаю. Нет ничего плохого в том, чтобы опубликовать такой вопрос, который должен быть известен и который нелегко найти, а затем быстро опубликовать ответ. Проблема возникает, когда вы отправляете ответ и быстро принимаете свой ответ. Отправив ответ сразу, другие увидят ответ, а затем извлекут из него уроки, в основном это новички в этой теме, и, не принимая ваш ответ, вы следуете обычной практике в SO. :)   -  person Guy Coder    schedule 07.06.2019
comment
Если другие хотят добавить свой ответ, они могут это сделать, если увидят, что ответ не принят. Дело не в том, чтобы помочь другим учиться.   -  person Guy Coder    schedule 07.06.2019


Ответы (2)


По поводу отслеживаемости дизъюнкции. Эти два запроса структурно различаются:

trace, (true -> X=a); X=b.

trace, ((true -> X=a); X=b).

Вы можете использовать write_canonical / 1, чтобы увидеть разницу:

?- write_canonical((trace, (true -> X=a); X=b)), nl.
;(','(trace,->(true,=(A,a))),=(A,b))
true.

?- write_canonical((trace, ((true -> X=a); X=b))), nl.
','(trace,;(->(true,=(A,a)),=(A,b)))
true.

А еще они по-разному ведут себя:

?- trace, (true -> X=a); X=b.
   Call: (9) true ? creep
   Exit: (9) true ? creep
   Call: (9) _428=a ? creep
   Exit: (9) a=a ? creep
X = a 
   Call: (9) _428=b ? creep
   Exit: (9) b=b ? creep
X = b.

[trace]  ?- trace, ((true -> X=a); X=b).
   Call: (9) true ? creep
   Exit: (9) true ? creep
   Call: (9) _706=a ? creep
   Exit: (9) a=a ? creep
X = a.

Только второй запрос будет проверять (true -> X = a); Х = б.

person Mostowski Collapse    schedule 08.06.2019
comment
Я также спросил на форуме SWI, и, похоже, это была ошибка пользователя и с моей стороны - я нажал Enter, чтобы подкрасться, но не ввел ;, чтобы получить другой ответ. - person lambda.xy.x; 08.06.2019
comment
Я также использовал trace. для перехода в режим трассировки и ввел запрос отдельно. - person lambda.xy.x; 08.06.2019

Сначала давайте обратимся к исходному предикату: форматирование предполагает вложенный if-then-else, но круглые скобки вокруг второго тела группируют первый true в ветку else:

?- listing(p).
p(A, B) :-
    (   cond1(A, B)
    ->  q(A)
    ;   true,
        (   cond2(A, B)
        ->  q(B)
        )
    ;   true
    ).

Вот так мы получаем запросы, начинающиеся с true, .... Вторая проблема заключается в том, что использование ; перегружено: термин формы G1; G2 интерпретируется как дизъюнкция, только если G1 не имеет формы (Cond -> Goal). В противном случае это интерпретируется как «если-то-еще». Давайте посмотрим на разные случаи:

  • (true, true -> X=a); X=b интерпретирует ; как дизъюнкцию, потому что самый внешний функтор левой части - это конъюнкция ,. Prolog сообщает о подстановке ответов для каждой ветви.
  • (true -> X=a, true); X=b: дизъюнкция по той же причине
  • (true -> X=a); X=b: это if-then-else, потому что самый внешний функтор левой части - это оператор if-then ->. Пролог сообщает только о замене ответа для ветки true.

Интересно, что когда мы помещаем условие в переменную, это, кажется, больше не работает (в SWI 8):

?- G1 = (true -> (X = a)), (G1 ; X=b).
G1 =  (true->a=a),
X = a ;
G1 =  (true->b=a),
X = b.

То же самое происходит, когда я оборачиваю G1 в call/1:

?- G1 = (true -> (X = a)), (call(G1) ; X=b).
G1 =  (true->a=a),
X = a ;
G1 =  (true->b=a),
X = b.

Если я правильно прочитал ответ на предыдущий вопрос, первый вопрос должен быть другим.

Я бы предположил, что другое поведение трассировки заключается в том, что точки останова мешают обнаружению if-then-else. Моя ошибка во время трассировки заключалась в том, что я нажимал enter для ползучести, но я не понимал, что мне нужно ввести ;, когда фактический ответ был сообщен.

person lambda.xy.x    schedule 06.06.2019
comment
Я не уверен, что согласен с интерпретацией, что ;/2 перегружен. В случае g1 -> g2 ; g3, g1 -> g2 по определению исключает точку выбора в g1. Для вызова g3 должна быть точка выбора, которая принимает второй аргумент ;/2, если первый аргумент терпит неудачу. Вот почему я удивлен, что (true -> X=a, true); X=b дает 2 результата, как я уже упоминал в своих комментариях к вопросу. - person lurker; 07.06.2019
comment
У меня возникла идея из загадочного замечания в документации SWI для - ›. Кажется, этот термин действительно должен иметь форму ; ( -> (g1, g2), g3). Возможно, я ошибаюсь, но разве не круглые скобки у термина ( (true -> X=a), true )? Это сделало бы , дочерним функтором ;, и это не определение if-then-else, которому мы должны следовать. - person lambda.xy.x; 07.06.2019
comment
Да, ты прав. ((true -> X=a), true) ; X=b не в форме if-then-else. Но поведение ->/2 должно быть любым, поэтому он должен избавиться от точки выбора на первом true (или я бы так подумал). Вот почему я удивлен, что X=b показан как решение. - person lurker; 07.06.2019
comment
Всегда используйте listing/1 для установки круглых скобок вправо. - person false; 07.06.2019
comment
I would assume that the different tracing behaviour is that the breakpoints interfere with the detection of if-then-else. Если вы спросите об этом в SWI-Prolog Discourse, скорее всего, Ян ответит. - person Guy Coder; 07.06.2019
comment
@false Я использовал write_canonical / 1, но не думал о листинге - write_canonical в этом случае не печатает круглые скобки. - person lambda.xy.x; 07.06.2019