有人可以解释有关断言和撤消的Prolog逻辑视图吗?
例如,在下面的代码中,在第一次运行时,Prolog返回true,在后续运行中返回false。我不知道为什么因为asserta(nextBound(100))
满足时的Prolog逻辑视图,nice(X)在启动时仍然被值冻结,所以这个更改应该被忽略而且nextbound(100)
必须是false。 / p>
nextBound(10000).
nice(X) :-
asserta(nextBound(100)),
retract(nextBound(10000)),
nextBound(100).
答案 0 :(得分:5)
您可以执行trace
来确定会发生什么:
| ?- nice(_).
1 1 Call: nice(_17) ?
2 2 Call: asserta(nextBound(100)) ?
2 2 Exit: asserta(nextBound(100)) ? <-- 1st assert of netBound(100) succeeds
3 2 Call: retract(nextBound(10000)) ?
3 2 Exit: retract(nextBound(10000)) ? <-- retract nextBound(10000) succeeds
4 2 Call: nextBound(100) ?
4 2 Exit: nextBound(100) ? <-- Succeeds because netBound(100) now a fact
1 1 Exit: nice(_17) ?
(1 ms) yes
{trace}
| ?- nice(_).
1 1 Call: nice(_17) ?
2 2 Call: asserta(nextBound(100)) ?
2 2 Exit: asserta(nextBound(100)) ? <-- 2nd assert of netBound(100) succeeds
3 2 Call: retract(nextBound(10000)) ?
3 2 Fail: retract(nextBound(10000)) ? <-- retract nextBound(10000) fails
1 1 Fail: nice(_17) ?
(3 ms) no
{trace}
| ?-
你可以看到,在第一种情况下,首先成功断言nextBound(100)
事实(第一次)。然后,retract(nextBound(10000))
成功,因为nextBound(10000).
是数据中存在的事实。在此之后,查询nextBound(100)
成功,因为在此事实之前的两个步骤被声明为数据。
在nice(_)
的第二次执行时,nextBound(10000)
不存在,因为它在第一次执行时被收回,并且代码不会重新断言它。因此,在nice(_)
的第二次执行中,retract(nextBound(10000))
失败,因为事实nextBound(10000)
不存在,并且nice(_)
的整个第二次执行在此时失败,因为回溯{ {1}}和asserta
不会重新执行并产生其他结果。
列表显示现在有两个retract
个事实,因为我们在nextBound(100)
和nice(_)
的两次运行中都断言了一个,因为它已被收回首次运行nextBound(10000)
:
nice(_)
逻辑更新视图,如SWI文档中所述,从SWI-Prolog 3.3.0开始,我们遵循逻辑更新视图,其中backtrackable谓词输入谓词的定义< / strong>不会看到任何更改(由| ?- listing.
% file: user
nice(_) :-
asserta(nextBound(100)),
retract(nextBound(10000)),
nextBound(100).
% file: user_input
nextBound(100).
nextBound(100).
(1 ms) yes
| ?-
或assert/1
引起)到谓词。
换句话说,逻辑更新视图会阻止谓词在执行时动态更改本身。那里的不是场景。
事实上,在Prolog中,在执行谓词期间,如果在谓词中的某一点声明事实,那么结果必须立即在其中可见,或者谓词可能无法正常运行。有许多常见的库谓词依赖于这种行为。
答案 1 :(得分:5)
从历史视图开始,逻辑更新视图首先在Quintus 2.0(其当前的后继者是SICStus)中实现,并在1987年的文献中进行了描述。它已在ISO Prolog ISO / IEC 13211-1中采用: 1995年。主要的想法是,动态谓词的任何目标都将完全考虑那些在执行目标时出现的子句。任何进一步的更改 - 无论是添加还是删除 - 都不会被考虑用于执行该目标。
在逻辑更新视图之前,存在各种或多或少一致的实现,大多数与Prolog系统的各种优化不兼容。请注意,差异仅显示您是否有可能有多个答案的目标。无论是作为简单目标,还是使用撤消和,您都使用retract/1
或assertz/1
。仅使用asserta/1
时不会显示差异。所以你的例子无法澄清差异。
考虑动态谓词p/1
。由于以下交互仅使用顶层,我将通过声明一个事实并立即撤回p/1
的所有事实来使系统知道p/1
。此外,在开始下一个查询之前,我将使用retractall(p(_))
删除所有事实。
?- asserta(p(1)).
true. % now p/1 is known to the system.
?- retractall(p(_)), assertz(p(1)), p(X), assertz(p(2)).
X = 1. % only one answer!
?- retractall(p(_)), assertz(p(1)), p(X), assertz(p(2)), p(Y).
X = 1, Y = X ;
X = 1,
Y = 2.
因此,第一个目标p(X)
仅看到p(1)
,而第二个目标p(Y)
同时看到?- retractall(p(_)), assertz(p(1)), assertz(p(2)), p(X), assertz(p(3)), p(Y).
X = 1, Y = X ;
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2, Y = X ;
X = 2,
Y = 3 ;
X = 2,
Y = 3 ;
false.
。这适用于任何有效目标:
X
再次注意p(X)
只有1或2但不是3。
或者,您可以想象每个目标... findall(Xi, p(Xi), Xis), member(X, Xis) ...
被替换为:
p/1
这向您展示了背后的想法:从概念上讲,所有答案都是暂时存储的,然后才会显示每个答案。
呃,上面的情况并不完全正确,因为只有 ... findall(Xi-Bi, clause(p(Xi),Bi), XiBis), member(X-B,XiBis), B ...
的子句以这种方式处理。也就是说,只要你只存储事实,上面的解释是完美的,但如果你也存储规则,你需要一个更复杂的解释,粗略地说:
retract/1
同样,这不是明确的事实,因为一些更奇特的问题,如削减可能会介入。我将暂时离开 1 。
同样,?- retractall(p(_)),
assertz(p(1)), assertz(p(2)),
retract(p(X)), ( X = 1, retract(p(Y)) ; X = 2, Y = none ).
X = 1,
Y = 2 ;
X = 2,
Y = none.
也会查看和删除它在执行时看到的子句。在大多数情况下,这非常直观,并且符合我们的期望。然而,有如下荒谬的情况:
p(2)
此处,事实p(2).
已删除两次,但数据库只包含一个事实... p(X) ...
1实际上,替换
... findall(Xi-Bi, clause(p(Xi),Bi), XiBis), answs_goal_x(XiBis,X, G), G ...
通过
answs_goal_x([], _, true).
answs_goal_x([Xi-Bi|XiBis], X, ( X = Xi, Bi ; G) ) :-
answs_goal_x(XiBis, X, G).
与
{{1}}