Будет ли использование member внутри предложения forall в SWI-Prolog всегда выводить элементы в одном и том же порядке?

Недавно познакомившись с Prolog, я использовал его для нескольких простых задач и начал задумываться об использовании member внутри циклов forall, подобных тому, который показан в тривиальном примере ниже:

forall(member(A,[1,2,3,4]), print(A)).

В случае, если вы делаете что-то подобное, всегда ли верно, что forall будет обрабатывать элементы в списке в одном и том же порядке каждый раз при его вызове? Должно ли это быть принудительно, скажем, выполнив что-то вроде:

A = [1,2,3,4], sort(A, B), forall(member(C,B), print(C)).

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

Может ли кто-нибудь дать мне какие-либо гарантии или объяснения по этому поводу?


person Jonathan Rainer    schedule 03.02.2014    source источник
comment
Я думаю, вы можете положиться на это, потому что списки Пролога односвязны, и любой другой обход будет работать хуже. Я, конечно, не вижу никакой семантической разницы в том, чтобы сначала назвать список.   -  person Daniel Lyons    schedule 03.02.2014


Ответы (3)


Недетерминизм в Прологе просто относится к предикату, потенциально имеющему более одного решения. Ясно, что member/2 - такой предикат. Это не означает, что вы должны беспокоиться о том, что ваши вычисления станут непредсказуемыми. В Prolog есть четко определенное правило вычислений, которое, по сути, гласит, что альтернативные решения исследуются в глубину слева направо. Таким образом, ваша цель member(X,[1,2,3,4]) будет генерировать решения для X в ожидаемом порядке 1,2,3,4.

Сортировка списка [1,2,3,4] не будет иметь никакого значения, поскольку он уже отсортирован (в соответствии со стандартным порядком терминов Пролога).

Небольшое предостережение относительно forall/2: некоторые Прологи определяют это, но это, вероятно, менее полезно, чем вы думаете, потому что на самом деле это не «цикл». Вы можете использовать его в своем примере, потому что вы выполняете только побочный эффект печати на каждой итерации. Для большинства других целей вам следует ознакомиться с рекурсивными шаблонами, такими как

print_list([]).
print_list([X|Xs]) :- print(X), print_list(Xs).
person jschimpf    schedule 04.02.2014
comment
+1 хорошо, может быть, это скорее то, что OP имел в виду - person false; 05.02.2014
comment
Спасибо @jschimpf, теперь это имеет смысл. Также спасибо за совет по рекурсии, это был больше вопрос теории того, что происходит при использовании forall, но я знаю, что в целом рекурсия - лучшая идея. - person Jonathan Rainer; 05.02.2014
comment
Это не так. Недетерминированный алгоритм, см. также этот question. Кортеж фактов является детерминированным, потому что при вызове он всегда возвращает один и тот же результат. Элемент является полудетерминированным, потому что он дает одинаковый результат для одного и того же входа. Недетерминированный означает потенциально разные выходные данные для одних и тех же входов между прогонами, как при генерации случайных чисел для заданного диапазона. - person G_V; 25.07.2018

В N208 для всех / 2 определены + (вызов (Генератор), + вызов (Тест)), поэтому это делает его менее сомнительным. Но в силу того, что основной стандарт ISO (+) / 1 уже выполняет вызов / 1 и что основной стандарт ISO (,) / 2 будет подвергаться преобразованию тела, можно просто определить его следующим образом в базовом стандарте ISO Prolog:

forall (Генератор, Тест): -
\ + (Генератор, \ + Тест).

SWI-Prolog также реализовал этот способ, и ошибка, обнаруженная Ульрихом Ноймеркелем, не будет видна при использовании forall / 2:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.

?- \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
nonconforming
true.

?- forall(C=!, (C,fail;writeq(nonconforming))).
false.

Дополнительное замечание:

Я не знаю, насколько это полезно для цикла. Мне кажется, что использование его для циклов - неправильный подход, так как тест может дать сбой, а затем и конструкция. Я также видел, как Стригниц и Блэкберн следующее определение вспомогательного предиката, который они называют циклом, управляемым отказом.

doall (Гол): -
Гол, провал.
doall (_).

Я обнаруживаю, что прямо пишу Goal, fail; true, который тоже выполняет свою работу:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.

?- member(A,[1,2,3,4]), write(A), nl, fail; true.
1
2
3
4
true.
person Mostowski Collapse    schedule 25.07.2018

Строго говоря, в SWI нет гарантии на нескольких уровнях:

1mo, что member/2 или forall/2 будут работать именно так, поскольку вы можете их переопределить.

?- [user].
member(X,X).
|: % user://1 compiled 0.00 sec, 2 clauses
true.

?- forall(member(A,[1,2,3,4]), print(A)).
[1,2,3,4]
true.

Однако member/2 определяется в прологе Пролога, который охватывает все детали, которые вас интересуют. Что касается forall(A,B), безопаснее вместо этого писать \+ (A, \+B), поскольку это зависит только от стандартных функций. Нет никакого определения forall/2 как такового, поэтому трудно сказать, что является «правильным» поведением.

2до того, что SWI будет соответствовать стандарту. Если вы прочитаете документацию, вы заметите, что не существует самодекларации (как, например, SICStus Prolog) для соответствия стандарту. Фактически, \+ (A, \+B) не полностью соответствует, как в следующем примере, который должен автоматически завершиться ошибкой, а скорее печатает nonconforming

?-  \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
person false    schedule 03.02.2014
comment
forall/2 определяется как встроенный предикат в B-Prolog, BinProlog, CxProlog, GNU Prolog, JIProlog, Lean Prolog, LPA WinProlog / MacProlog, Qu-Prolog, SWI-Prolog, XSB, YAP. Это также было указано в проекте предложения по пересмотру ядра Пролога ISO от октября 2009 года. За него проголосовали за включение в стандарт на заседании WG17 в 2009 году. Похоже, через некоторое время он был исключен. Но это не мешает считать его стандартным де-факто предикатом. - person Paulo Moura; 04.02.2014
comment
@PauloMoura: За исключением GNU Prolog, ни один в вашем списке не объявляет себя как ISO, что оставляет открытым вопрос о том, что именно может означать определение forall / 2. Например. YAP: forall((fail,2),true) выполняется успешно, а не вызывает ошибку. - person false; 04.02.2014
comment
Только топ на YAP. Скомпилируйте тот же вызов в теле предложения предиката, определенного в файле, и вы получите ошибку времени компиляции. Подобные пограничные случаи для большинства сценариев использования не являются проблемой. - person Paulo Moura; 04.02.2014
comment
@PauloMoura: Значит, YAP не соответствует ISO + определение в N208. И не ведет себя последовательно. Edge case не является понятием ISO или N208. - person false; 04.02.2014
comment
Это ошибка SWI7, что ваш запрос пишет несоответствие. Думаю, мне следует проголосовать еще раз. Я сначала неправильно понял твой пост. Интересная находка. Прекрасно работает в моей системе, в прологе ECLiPSe, в SICStus Prolog и т. Д., Все дают правильный No. - person Mostowski Collapse; 25.07.2018