1

我正在使用以下代码临时更改发生检查标志,其中 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 工作,并将发生检查标志设置为 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 有一个带有剩余选择点的出口端口,setup_call_cleanup/3将不会调用它的清理。因此发生的检查标志更改将泄漏到延续中:

?- 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

有没有更安全的方法来临时更改发生检查?

4

1 回答 1

2

也许在 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.
于 2021-02-04T15:19:44.640 回答