Prolog assert / 1与它通过的术语有什么关系?

时间:2019-05-01 22:25:02

标签: prolog

我试图了解断言和撤消与传递它们的术语有何关系。如果我运行以下命令:

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

这一切让我觉得我遗漏了某些东西,或者这些内置函数的设计不一致吗?任何可以解释的说明都很好。

3 个答案:

答案 0 :(得分:2)

我模糊地记得有一个retractall/1retract/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)异常。下面的示例添加了两个事实和一条规则。注意规则周围的双括号。

因此,它不会调用或推断任何东西!这只是事实和规则进入活动内存的断言。