Prolog 如何安全地临时更改发生检查标志?

Prolog 如何安全地临时更改发生检查标志?,prolog,occurs-check,Prolog,Occurs Check,我正在使用以下代码临时更改occurs检查标志,其中G是一个不会创建任何延迟目标的目标: with_occurs_check(G) :- current_prolog_flag(occurs_check, F), setup_call_cleanup( set_prolog_flag(occurs_check, true), G, set_prolog_flag(occurs_check, F)). 下面是with_occurs_check/1的

我正在使用以下代码临时更改occurs检查标志,其中G是一个不会创建任何延迟目标的目标:

with_occurs_check(G) :-
   current_prolog_flag(occurs_check, F),
   setup_call_cleanup(
      set_prolog_flag(occurs_check, true),
      G,
      set_prolog_flag(occurs_check, F)).
下面是with_occurs_check/1的一个理想方面,因为我们希望所有回溯到目标G工作时,将occurs check标志设置为true。最好只创建一个选择点,这可以在SWI Prolog中看到:;只问一次,而不是两次:

?- with_occurs_check((X=1;current_prolog_flag(occurs_check, F), X=2)).
X = 1 ;
X = 2,
F = true.   %%% desired
但有一个警告。当G是非确定性的时,上述方法实际上不起作用。如果G有一个带有剩余选择点的出口端口,则不会调用其清理。因此,occurs check标志更改将泄漏到延续中:

?- with_occurs_check((X=1;X=2)), current_prolog_flag(occurs_check, F).
X = 1,
F = true ;   %%% not desired, update leaking
X = 2,
F = false.   %%% desired

是否有更安全的方法临时更改发生检查?

可能在G成功后也恢复发生检查

with_occurs_check(G) :-
   current_prolog_flag(occurs_check, F),
   setup_call_cleanup(
      set_prolog_flag(occurs_check, true),
      (G, set_prolog_flag(occurs_check, F)),
      set_prolog_flag(occurs_check, F)).
试运行:

?- with_occurs_check((X=1;X=2)), current_prolog_flag(occurs_check, F).
X = 1,
F = false ;
X = 2,
F = false.
在阅读注释后更新,以便在回溯时继续进行检查:

with_occurs_check(G) :-
   current_prolog_flag(occurs_check, F),
   setup_call_cleanup(
      set_prolog_flag(occurs_check, true),
      (G, with_occurs_check1(F)),
      set_prolog_flag(occurs_check, F)).

with_occurs_check1(F):-
  set_prolog_flag(occurs_check, F).
with_occurs_check1(_):-
  set_prolog_flag(occurs_check, true),
  fail.
样本运行:

?- with_occurs_check((X=1;current_prolog_flag(occurs_check, F), X=2)), current_prolog_flag(occurs_check, F2).
X = 1,
F2 = false ;
X = 2,
F = true,
F2 = false ;
false.

可能在G成功后也恢复发生检查

with_occurs_check(G) :-
   current_prolog_flag(occurs_check, F),
   setup_call_cleanup(
      set_prolog_flag(occurs_check, true),
      (G, set_prolog_flag(occurs_check, F)),
      set_prolog_flag(occurs_check, F)).
试运行:

?- with_occurs_check((X=1;X=2)), current_prolog_flag(occurs_check, F).
X = 1,
F = false ;
X = 2,
F = false.
在阅读注释后更新,以便在回溯时继续进行检查:

with_occurs_check(G) :-
   current_prolog_flag(occurs_check, F),
   setup_call_cleanup(
      set_prolog_flag(occurs_check, true),
      (G, with_occurs_check1(F)),
      set_prolog_flag(occurs_check, F)).

with_occurs_check1(F):-
  set_prolog_flag(occurs_check, F).
with_occurs_check1(_):-
  set_prolog_flag(occurs_check, true),
  fail.
样本运行:

?- with_occurs_check((X=1;current_prolog_flag(occurs_check, F), X=2)), current_prolog_flag(occurs_check, F2).
X = 1,
F2 = false ;
X = 2,
F = true,
F2 = false ;
false.

您希望为证明子树启用发生检查。但是启用了occurs标志的Prolog处理器实际上是另一个引擎,特别是在启用occurs检查的上下文中可以运行任意延迟目标。在SWI Prolog中,您可以开始。。。但我仍然不确定occurs标志是否仅限于此。@David Tonhofer我已将Prolog引擎推测和协同路由问题分为一个单独的问题,因为协同路由实际上不在当前问题的范围内,既然问题说G是一个不会产生任何延迟目标的目标,那么您希望对证明子树启用发生检查。但是启用了occurs标志的Prolog处理器实际上是另一个引擎,特别是在启用occurs检查的上下文中可以运行任意延迟目标。在SWI Prolog中,您可以开始。。。但我仍然不确定occurs标志是否仅限于此。@David Tonhofer我已将Prolog引擎推测和协同路由问题分为一个单独的问题,因为协同路由实际上不在当前问题的范围内,因为问题说G是一个不会产生任何延迟目标的目标,它不会通过这个测试用例:?-with_occurses_checkX=1;当前的prolog标志发生检查,F,X=2。回溯时,结果应为F=真。我已经在问题中添加了测试用例,因为这显然不是一个要求。@MostowskiCollapse:check updated answer这是一个有趣的解决方案。还有一个令人困扰的问题。with_occurrence_check/1现在总是留下一个选择点,因为with_occurrence_check 1/1会这样做。您可以尝试使用_-occurses_-checkX=1;X=2,当前\u prolog\u flag\u check,F。再次,它将请求;两次,而不仅仅是一次。它没有通过此测试用例:?-with\u occurses\u checkX=1;当前的prolog标志发生检查,F,X=2。回溯时,结果应为F=真。我已经在问题中添加了测试用例,因为这显然不是一个要求。@MostowskiCollapse:check updated answer这是一个有趣的解决方案。还有一个令人困扰的问题。with_occurrence_check/1现在总是留下一个选择点,因为with_occurrence_check 1/1会这样做。您可以尝试使用_-occurses_-checkX=1;X=2,当前\u prolog\u flag\u check,F。再次,它将请求;两次,而且不止一次。