我试图理解断言和撤回对它们传递的术语的作用。如果我运行以下内容:
?- assertz(item(first)).
true.
?- assertz(item(second)).
true.
?- assertz((item(Y) :- =(Y, third), writeln(Y))).
true.
?- retract(item(X)).
X = first ;
X = second ;
false.
retract / 1删除了所有项目,但我的writeln / 1从未被调用,因此它显示retract实际上并不解析它传递的术语。看起来它正在做一些特殊操作:
这是正确的吗?或者是其他事情发生在这里?
换句话说:如果我编写自己的单个参数规则,Prolog不会自动将项目(X)与数据库统一,并遍历所有第1项的事实。它只给我项目(X)。如何缩回它的魔力?
?- assertz((myRule(X) :- writeln(X))).
true.
? myRule(item(X)).
item(_2556)
true.
回答进一步澄清:根据User9213的回答,似乎答案是“撤回(但不断言!)有一个有趣的行为,它在对数据库执行某些操作之前对其术语进行简单统一”。我想知道为什么我见过的其他内置函数(如atom / 1和writeln / 1)似乎没有这样做。也许它比我经历的更常见?
为了保持一致性,似乎retract应该有必要的基础术语,如果用户想要做我上面的例子,他们可以做到这一点:
? - (项目(X),撤回(项目(X)))。
这一切都让我觉得我错过了一些东西,或者这些内置功能只是设计不一致?任何可以解释这一点的澄清都会很棒。
为了将我的声音添加到合唱中,是的,所有这些谓词(assert*
,retract*
)操纵Prolog数据库中的事实和规则;他们不会将他们的论点评估为事实或规则。是的,他们只是在他们的论点与数据库中的事实和规则之间进行“句法”统一。
Prolog是一个homoiconic language。该程序可以像操作数据一样进行操作;数据结构可以解释为它们是程序。最重要的是:某个结构是数据还是程序,仅取决于上下文。
你似乎明白了这一点,但对于下一个可怜的灵魂来说,你的问题和这个答案是偶然的,这里有一个玩具示例来展示。
有一个built-in predicate atom/1
成功,如果它的论证是,原子:
?- atom(foo).
true.
?- atom(Bar). % Bar is a free variable, which is not an atom
false.
?- atom(atom(bar)). % atom(bar) is a compound term, which is not an atom
false.
但atom(bar)
只是最后一个查询中的复合词。它是一个带有仿函数原子/ 1的复合词。它唯一的论点是原子。所以,如果您现在查询它:
?- atom(bar).
true.
还有这个非常好的谓词called call
。它使模糊程序和数据之间的界限变得更加容易。以下三个查询的含义相同:
?- atom(bar).
true.
?- call(atom, bar).
true.
?- call(atom(bar)).
true.
此外,您可以动态(在运行时)创建数据结构并将其评估为程序。这是一个简单的call/2
实现,可以评估谓词,提供谓词名称和参数列表。它constructs the compound term using "univ"并通过将它放在一个子区域应该是的插槽中来评估它。我将其命名为my_call/2
,我将使用assertz
将其添加到数据库中:
?- assertz((my_call(Name, Args) :- Goal =.. [Name|Args], Goal)).
true.
?- my_call(between, [1, 3, X]).
X = 1 ;
X = 2 ;
X = 3.
你看到发生了什么事吗? (注意:似乎call
是Prolog的一个相对较新的补充。在call
被广泛使用之前,你必须做这个技巧,如果你想元调用谓词。我不知道这是从经验,只是基于的教育猜测我读过或听过的东西,现在不能引用它们。)
所以,让我们再试一次。实际上事情更复杂,但非常简单:
Prolog程序是一组谓词。没有订购!
每个谓词都是一个子句列表。它是一个列表,因为谓词可能有0个或更多个子句,并且它们有顺序。
条款可以是事实或规则。
规则是带有仿函数:-/2
的复合词。是的,这是对的。看看我的意思:
?- assertz(:-(foo(X), between(1, 3, X))).
true.
?- listing(foo/1).
:- dynamic foo/1.
foo(A) :-
between(1, 3, A).
true.
这只是写它的另一种方式。它更传统。
确实,这两者非常不同:
foo(X). % a fact
foo(X) :- between(1, 3, X). % a rule
你无法将这一个与另一个统一起来。这就是为什么,如果你想收回foo(X) :- between(1, 3, X)
,你不能只通过foo(X)
作为撤回的参数。或者,要完成您的示例:
?- assertz(item(first)).
true.
?- assertz(item(second)).
true.
?- assertz((item(Y) :- =(Y, third), writeln(Y))).
true.
?- retract(item(X)).
X = first ;
X = second ;
false.
?- retract((item(X) :- X = Y, writeln(X))).
Y = third.
你现在看到了吗?
我有一个模糊的记忆,有一个retractall/1
与retract/1
并存,事实证明它是这样的情况。 retract/1
接受Prolog术语,正如文档所述:
缩回(+期限)
当Term是一个原子或一个术语时,它与数据库中的第一个统一事实或子句统一起来。事实或子句将从数据库中删除。
强调我加入了。这由以下说明:
?- asserta(foo(X) :- X = 2 ; X = 4).
true.
?- foo(X).
X = 2 ;
X = 4.
?- retract(foo(X)).
false.
?- foo(X).
X = 2 ;
X = 4.
?- retract(foo(X) :- X = 2 ; X = 4).
true.
?- foo(X).
false.
请注意,如果您提供给asserta/1
的完整条款,它将被撤回。正如@CapelliC在下面指出的那样,你也可以提供一个带有变量的子句作为参数来用物体收回东西:
retract(foo(X) :- Y).
但是,如果您确实要撤消与模式匹配的内容,可以使用retractall/1
,文档说明:
retractall(+头)
数据库中与Head统一的所有事实或条款都将被删除。
这由以下说明:
?- asserta(foo(X) :- X = 2 ; X = 4).
true.
?- foo(X).
X = 2 ;
X = 4.
?- retractall(foo(X)).
true.
?- foo(X).
false.
这两者之间的另一个重要区别是,retract/1
将一次删除一件事,它将统一这些事情:
?- asserta(foo(X) :- X = 2).
true.
?- asserta(foo(X) :- X = 4).
true.
?- retract(foo(X) :- Y).
Y = (X=4) ;
Y = (X=2) .
这与retractall/1
相反,?- asserta(foo(X) :- X=2).
true.
?- asserta(foo(X) :- X=4).
true.
?- foo(X).
X = 4 ;
X = 2.
?- retractall(foo(X)).
true.
?- foo(X).
false.
将删除与模式匹配的所有内容,而不会统一任何内容:
retract/1
因此,retractall/1
实际上是为了做一次一次的撤消,但期望形状像你断言的那样,而definition of assert
in swi-prolog只想要一个头,把它当作一个模式,并将删除尽可能多的东西匹配模式。
这无疑帮助我提高了对Prolog的理解,所以我希望它对你有所帮助!
qazxswpoi是:
将子句(事实或规则)置入数据库。谓词asserta / 1将该子句断言为谓词的第一个子句,而assertz / 1将该子句断言为最后一个子句。不推荐使用的断言/ 1等同于assertz / 1。如果目标模块的程序空间有限(请参阅set_module / 1),则asserta / 1可以引发resource_error(program_space)异常。下面的示例添加了两个事实和规则。请注意规则周围的双括号。
因此,它不会打电话或推断任何东西!这只是对事实和主动记忆规则的断言。