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

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

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

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

3 ответов


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

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

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

print_list([]).
print_list([X|Xs]) :- print(X), print_list(Xs).

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

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.

замечание:

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

от doall(цель) :-
   Цель-провал.
doall (_).

я оказываюсь прямо писать!--2--> который также выполняет эту работу:

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.

строго говоря, нет никакой гарантии в 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 как таковой, так это трудно сказать, что такое "правильное" поведение.

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

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