Во-первых, в документации clp(fd) упоминается:
В современных системах на Прологе арифметические ограничения включают и заменяют низкоуровневые предикаты над целыми числами. Основное преимущество арифметических ограничений состоит в том, что они являются истинными отношениями и могут использоваться во всех направлениях. Для большинства программ арифметические ограничения являются единственными предикатами, которые вам когда-либо понадобятся в этой библиотеке.
Во-вторых, в ранее заданном вопросе было упомянуто, что include/3 несовместим с clp(fd).
Означает ли это, что при написании пролога с помощью библиотеки clp(fd) можно использовать только операторы clp(fd) и предикаты clp(fd)?
Кроме того, например, почему include/3 несовместим с clp(fd)? Это потому, что не использует операторы clp(fd)? Чтобы использовать include/3 в коде clp(fd), нужно ли переписывать версию, в которой используются операторы и ограничения clp(fd)?
Include/3 не обязательно должен быть несовместим с clpfd (или любым другим использованием атрибутивных переменных ) - зависит от безопасного (для целей программы, а не обязательно "логически чистого") использования атрибутивных переменных. Например.:
:- use_module(library(clpfd)).
test_include(F) :-
L = [N1, N2, N3, N4],
N1 #< 2,
N2 #> 5,
N3 #< 3,
N4 #> 8,
include(gt_3_fd, L, F).
gt_3_fd(I) :-
I #> 3.
Результат в swi-прологе:
?- test_include(F).
F = [_A, _B],
_A in 6..sup,
_B in 9..sup.
Приведенный выше код безопасен, потому что переменные, используемые с clpfd, используются последовательно с clpfd, а указанные ограничения приводят к тому, что повторение gt_3_fd становится ненужным.
Как только переменные являются непеременными/заземленными в зависимости от варианта использования (clpfd работает с целыми числами, а не, например, с составными терминами, поэтому непеременная достаточно хороша), тогда переменные также можно использовать вне clpfd. Пример:
?- I = 5, I > 4, I @> 4, I #> 4.
I = 5.
Оператор , такой как >, использует фактическое значение переменной и игнорирует любые атрибуты, которые могли быть добавлены к переменной, например. клпфд.
Логическая чистота, например. добавление ограничений к элементам в списке L после include/3, а не перед ним, является отдельной проблемой и также относится к переменным без атрибутов.
Вкратце: программа может использовать некоторые целые числа с clpfd и некоторые целые числа вне clpfd, т. е. смесь. Это нормально, пока различие между внутренним и внешним применяется последовательно, пока оно актуально (поскольку, например, маркировка будет давать фактические значения).
Да я вижу. Если я правильно понимаю, я могу использовать любой предикат, если я непротиворечив в переменных и операторах, которые я использую с ними. Просто чтобы уточнить, скажем, я использую предикат из библиотеки p(X) :- X > 5., смогу ли я вызвать его из моего кода clp(fd) с помощью X in 1..5, p(X). или это не сработает, потому что предикат p не использует оператор сравнения #>?
X > 5
выдает ERROR: Arguments are not sufficiently instantiated
, если X в этот момент имеет значение var - swi-prolog.org/pldoc/doc_for?object=var/1Как вы можете отличить приведенный выше случай от «незначительного» варианта, скажем, N4 #< 8, где вы получаете неправильный результат?
@false Я бы обвинил программиста в том, что он не использует овеществление, если овеществление (например, tfilter вместо include, как вы упомянули) требуется в этот момент для правильной работы программы :-) Или, возможно, перепишите программу, чтобы использовать include после переменных были созданы экземпляры.
В том то и смысл, что они несовместимы.
@false Я думаю, наша разница в том, что меня интересует программа, которая работает (т.е. отвечает заявленным требованиям), тогда как вы продвигаете логически чистый стиль, который имеет свое применение, но для большинства вопросов о Прологе на этом сайте , является излишним и показал бы Пролог новичкам слишком сложным и медленным.
Проблема заключается в следующем: если вы получаете какой-либо ответ от Пролога, можете ли вы полагаться на результат или вам нужно воспроизводить и проверять каждый крошечный шаг, выполненный Прологом.
...так что это не имеет ничего общего с чистым стилем как таковым. Подумайте о (=\=)/2, который просто безопасен, но чрезвычайно ограничен.
Когда мы говорим о «совместимости», мы всегда имеем в виду «совместимость в общем случае», мы никогда не имеем в виду «совместимость иногда» или «совместимость при благоприятных условиях». Таким образом, include/3 совместим с clpfd тогда и только тогда, когда он никогда не бывает с ним несовместим.
На первый взгляд код, который вы представляете, кажется логичным. Но если заглянуть глубже, он развалится. Почему? Это зависит от внутренних деталей реализации используемой вами библиотеки clpfd. Учтите, что (#>)/2 может оставить после себя остаточные цели. В вашем надуманном примере нет, а вообще может!
@repeat Заметьте, я не утверждаю, что include всегда безопасно использовать. Но говорить, что это всегда небезопасно, неправильно.
Конечно, но OTOH тоже никто не говорит, что «это всегда небезопасно». Центральный вопрос: на какие свойства может положиться пользователь include/3? Таким образом, в данном контексте «небезопасный» означает не «никогда не безопасный», а «не всегда безопасный». Пользователь не может полагаться на include/3 безопасность с clpfd (не зная деталей внутренней реализации, которые, кстати, могут быть изменены), поэтому мы говорим: «(в целом) include/3 небезопасно использовать с clpfd».
почему include/3 несовместим с clp(fd)?
?- X = 1, include(#\=(1),[0,X,2],Xs), X = 1.
X = 1,
Xs = [0,2]. % looks good
?- include(#\=(1),[0,X,2],Xs), X = 1.
false, unexpected.
?- include(#\=(1),[0,X,2],Xs). % generalization
Xs = [0,X,2],
X in inf..0/2..sup
; unexpected. % missing second answer
Итак, (#\=)/2 работает в этом случае, только если он достаточно конкретизирован. Как вы можете быть уверены, что это так? Ну, нет прямого безопасного теста. И, таким образом, вы получите неправильные результаты в определенных случаях. Пока эти примеры помещаются в одну строку, довольно легко обнаружить ошибку. Но с большей программой это практически невозможно. Из-за этого ограничения и include/3 несовместимы.
Выходом из ситуации было бы создание ошибок инстанцирования в случаях с недостаточным инстанцированием, но в контексте clpfd это довольно сложно. Другие встроенные предикаты, такие как (=\=)/2, делают это, и их применимость ограничена.
?- X = 1, include(=\=(1),[0,X,2],Xs), X = 1.
X = 1,
Xs = [0,2].
?- include(=\=(1),[0,X,2],Xs), X = 1.
instantiation_error. % much better than an incorrect answer
Безопасный вариант include/3 — это tfilter/3 из library(reif). Но прежде чем использовать его, я рекомендую вам прочитать это.
Вы видели Понимание кода CLP(FD) Prolog задачи N-ферзей, в частности утверждение в самом конце, those predicates generate constraints that are maintained internally ? Это должно дать вам еще некоторые необходимые знания.