Членство в списке пролога, возвращено несколько результатов

У меня есть стандартная процедура определения принадлежности к списку:

member(X, [X|_]).  
member(X, [_|T]) :- member(X, T).

Я не понимаю, почему, когда задаю следующий запрос:

?- member(a,[a,b]).

Результат

True;  
False.

Я бы подумал, что при достижении цели с использованием первого правила (поскольку a является заголовком списка) будет возвращено True, и это будет концом if. Кажется, что затем он пытается достичь цели, используя второе правило, и терпит неудачу?

Интерпретатор Пролога - это SWI-Prolog.


person Justin    schedule 30.01.2013    source источник
comment
какую версию SWI вы используете?   -  person Will Ness    schedule 30.01.2013
comment
@WillNess 6.2.6 64bit для Mac   -  person Justin    schedule 30.01.2013


Ответы (3)


Давайте сначала рассмотрим аналогичный запрос: [Изменить: сделайте это без добавления собственного определения; member/2 уже определено]

?- member(a,[b,a]).
true.

В этом случае вы получите оптимальный ответ: существует ровно одно решение. Но при обмене элементами в списке получаем:

?- member(a,[a,b]).
true ;
false.

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

Причина различия в том, что во втором запросе ответ true дается сразу после нахождения a в качестве элемента списка. Остающийся список [b] не содержит подходящего элемента, но он еще не исследован. Только по запросу (нажатие SPACE или ;) проверяется остальная часть списка, в результате чего больше нет решения.

По сути, эта небольшая разница дает вам подсказку, когда вычисление полностью завершено и когда еще есть над чем поработать. Для простых запросов это не имеет значения, но в более сложных запросах эти открытые альтернативы (точки выбора) могут накапливаться и использовать память.

Старшие руководители всегда спрашивали, хотите ли вы увидеть дальнейшее решение, даже если его не было.

Редактировать:

Возможность не спрашивать следующий ответ, если его нет, сильно зависит от самих деталей реализации. Даже в пределах одной системы и загруженной одной и той же программы вы можете получить разные результаты. Однако в этом случае я использовал встроенное определение SWI для member/2, тогда как вы использовали свое собственное определение, которое перезаписывает встроенное определение.

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

member(B, [C|A]) :-
        member_(A, B, C).

member_(_, A, A).
member_([C|A], B, _) :-
        member_(A, B, C).

Чтобы еще больше усложнить ситуацию: многие прологи имеют другой верхний уровень, который никогда не запрашивает дальнейших ответов, если запрос не содержит переменной. Таким образом, в этих системах (например, YAP) у вас складывается неправильное впечатление.

Попробуйте выполнить следующий запрос, чтобы увидеть это:

?- member(X,[1]).
X = 1.

SWI снова может определить, что это единственный ответ. А вот YAP, например, нет.

person false    schedule 30.01.2013
comment
На самом деле я до сих пор получаю верный и ложный ответ на оба этих вопроса. Во втором запросе я не понимаю, почему он будет продолжать попытки исследовать [b], когда первое правило уже нашло a в качестве заголовка списка. - person Justin; 30.01.2013
comment
@Justin Becasue, ты нажал кнопку ;. Он осмотрел голову, нашел, что она совпадает, и доложил вам true. Но он до сих пор не просмотрел остальную часть списка. - person Will Ness; 30.01.2013
comment
@WillNess, значит, вы говорите, что, хотя правило 1 удовлетворило запрос, правило 2 будет оценено (и, следовательно, проверяется конец списка [b] и не выполняется)? - person Justin; 30.01.2013
comment
Теперь я вижу, что я запускаю трассировку, что то, что вы говорите, правильно, оно пытается проверить member(a, [b]) и терпит неудачу. Кто-нибудь может объяснить, почему он это делает? Правильно ли я говорю, что при сопоставлении цели с заголовком правила оно вернется, когда цель будет достигнута, но затем все еще будет проверять другие правила с соответствующей заголовком при нажатии ';'? - person Justin; 30.01.2013
comment
@Justin Когда Prolog успешно удовлетворяет запрос, он не останавливается, а приостанавливается. Так определяется Пролог. Он делает паузу, запоминает, где находится, печатает результаты и ждет команды оператора: ';' или 'Пробел', 'Enter', '?' и Т. Д. - person Will Ness; 30.01.2013
comment
Я также получил истину и ложь по обоим этим запросам. И я думаю, что это ожидаемое поведение, потому что в member(a,[b,a]). после нахождения первого истина все еще остается пустой список для проверки. - person Sergii Dymchenko; 30.01.2013
comment
@WillNess, спасибо за разъяснения. Может, вы ответите еще на один вопрос. В качестве эксперимента я удалил второе правило, включил трассировку и дал запрос, который мог быть удовлетворен правилом 1. К моему удивлению, после возврата true и паузы я нажал ';' и то же правило проверяется второй раз, но не выполняется. Я понимаю, почему пролог может приостанавливаться, чтобы пользователь мог выбрать поиск дополнительных решений, но зачем проверять одно и то же правило дважды? - person Justin; 30.01.2013
comment
@Justin создайте чистый pl файл с соответствующим кодом и снова проверьте его. Если он ведет себя таким же образом, разместите этот код на каком-нибудь сайте кодовой пасты (hpaste.org очень легкий и быстрый) вместе с копией вашего сеанса взаимодействия. - person Will Ness; 30.01.2013
comment
@Justin: Извините за путаницу, я добавил некоторые пояснения. - person false; 30.01.2013

Вы используете ";" оператор после первого результата, затем нажмите return? Я считаю, что это просит запрос искать больше результатов, и, поскольку их нет, он считается ложным.

person Dan    schedule 30.01.2013
comment
Да. Однако мой опыт показывает, что обычно при создании запроса, который имеет только один возможный результат, он выводит только True, а затем возвращается к приглашению? -. The; Оператор обычно доступен только при нескольких ответах. - person Justin; 30.01.2013

Вы знаете про версию Prolog - !?

Если вы измените member(X, [X|_]). на member(X, [X|_]) :- !., Prolog не будет пытаться найти другое решение после первого.

person Sergii Dymchenko    schedule 30.01.2013
comment
Без сокращения он ведет себя в основном так, как вы описали в вопросе - затем пытается достичь цели, используя второе правило, и терпит неудачу. - person Sergii Dymchenko; 30.01.2013
comment
Я понимаю. В общем, если вы знаете, что более раннее правило - «лучшее решение», вы можете добавить сокращение. Обратной стороной является то, что вы никогда не узнаете, существовало ли другое интересное решение. - person Justin; 30.01.2013
comment
member(X,[1,2,3]) должен дать три ответа / решения, а не один. - person false; 30.01.2013
comment
@Justin: Представлять cut в программах Prolog очень сложно. Многие попытки, подобные приведенной выше, в некоторых случаях оказываются неудачными. Так что лучше избегать сокращений вначале, этого достаточно, чтобы научиться на сокращенных бесплатных программах. - person false; 30.01.2013