我试图了解断言和撤消与传递它们的术语有何关系。如果我运行以下命令:
?- 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从未被调用过,所以它似乎实际上不是 resolveing 传递它的术语。看起来它正在执行一些特殊操作:
这是对的吗?还是这里发生了其他事情?
用另一种方式说:如果我编写自己的单参数规则,则Prolog不会自动将item(X)与数据库统一,并且不会遍历item / 1的所有事实。它只是给我item(X)。缩进如何发挥其魔力?
?- assertz((myRule(X) :- writeln(X))).
true.
? myRule(item(X)).
item(_2556)
true.
需要进一步澄清的答案: 根据User9213的答案,答案似乎是“收回(但不能断言!)有一个有趣的行为,即在对数据库执行某些操作之前先对其术语进行简单的统一”。我不知道为什么我看到的其他内置函数(例如atom / 1和writeln / 1)似乎没有做到这一点。也许这比我经历的更为普遍?
为了保持一致性,似乎retract应该具有必要的基本条件,并且如果用户想要执行像我上面的示例这样的操作,则他们可以这样做:
?-(项目(X),收起(项目(X)))。
这一切让我觉得我遗漏了某些东西,或者这些内置函数的设计不一致吗?任何可以解释的说明都很好。
答案 0 :(得分:2)
我模糊地记得有一个retractall/1
与retract/1
并存,事实证明是这种情况。 retract/1
接受Prolog 条款,如文档所述:
撤回(+期限)
当Term是原子或术语时,它将与数据库中的第一个统一事实或子句统一。事实 or子句已从数据库中删除。
我增加了重点。 如下所示:
?- 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
,文档中指出:
收起全部(+头部)
与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
只想要一个头部,将其视为模式,并且将删除与模式匹配的内容。
这肯定有助于我加深对Prolog的理解,因此希望对您有所帮助!
答案 1 :(得分:2)
是的,要将我的声音添加到合唱中,是的,所有这些谓词(assert*
,retract*
)操纵着Prolog的事实和规则数据库;他们不评估其论点,就好像它们是事实或规则一样。是的,他们只是在论据与数据库中的事实和规则之间进行“句法”统一。
序言是homoiconic language。该程序可以像处理数据一样进行操作。可以将数据结构解释为程序。最重要的是:某个结构是 data 还是 program 仅取决于上下文。
您似乎理解了这一点,但是对于下一个偶然发现您遇到的问题和答案的可怜人,下面是一个玩具示例。
如果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.
你现在看到了吗?
答案 2 :(得分:1)
definition of assert
in swi-prolog是:
在数据库中添加一个子句(事实或规则)。谓词asserta / 1断言该子句作为谓词的第一个子句,而assertz / 1断言该子句作为谓词的最后一个子句。 已弃用的assert / 1等同于assertz / 1 。如果目标模块的程序空间受到限制(请参阅set_module / 1),则asserta / 1会引发resource_error(program_space)异常。下面的示例添加了两个事实和一条规则。注意规则周围的双括号。
因此,它不会调用或推断任何东西!这只是事实和规则进入活动内存的断言。