Prolog中复杂术语的概念问题

6

我刚开始在大学学习Prolog,有一个概念问题我没有找到具体的答案:

我想要深入理解这种语言的"哲学",因此我想要非常准确地理解什么是复合术语(或者说复合项)。到目前为止,我读到过复合术语是一个函数符号和它的arity。我可以使用复合术语来构建知识库,就像这样:

love(john, sarah).

假设现在我可以输入任何可能的字符串对作为“love”的参数,除非字符串对是“john,sarah”,否则它将是false。所以,一个函子和它的参数的“问题”是,它是真还是假,取决于我是否已经明确地在我的知识库中说过这个问题,或者Prolog是否可以使用规则、统一等从他所拥有的信息中推断出来。
因此,从这里我理解到,复杂术语表示n个实体之间的关系是真或假的。但我不理解的是:
vertical(line(point(X,Y),point(X,Z))).

我理解point(X, Y)的意思是,任何实体都通过“point”与其他实体相关联。但我不明白的是,point(X, Y)如何成为line的参数!到目前为止,一个复杂的术语仅表示实体是否相关。我可以理解它是一个“传统函数”,如果实体相关,则返回true或false。但是,它怎么能成为line的参数呢?在理论上,“line”的参数有2个(实体),它会说它们是否相关。现在,参数的值是true还是false?
我可以理解“point(X,Y)”正在创建一个“点对象”。因此,line的参数是“点实体”。但这不是我到目前为止阅读关于复杂术语的内容,所以我希望有一个技术定义来解释嵌套案例。
(如果我使用了错误的术语或定义,请原谅,我是Prolog的新手)
谢谢!
3个回答

5
这是一个很好的问题,因为它是Prolog的一个基本混淆点,许多Prolog初学者都来自命令式语言的背景。我通常在学习Prolog时的建议是:"忘记(几乎)你学过的所有编程知识,从基础开始"。
Prolog建立在项上,这些项可以有零个或多个参数。foo是一个没有参数的项。foo(A, B)是一个有两个参数的项。foo(bar(X), bah(Y,Z))是一个具有两个参数(称为foo/2)的复杂项,其参数由具有一个参数的项bar/1和具有两个参数的项bah/2组成。
即使谓词子句也是形如Head :- Body或规范形式':-'(Head, Body)的项。当这种类型的术语在Prolog程序中断言(静态声明在文件中,甚至是动态断言),Prolog将其识别为谓词,因为Prolog为一些术语函数赋予了特殊含义,在这种情况下,:-用于谓词子句定义。但是,如果没有这个上下文,':-'(A, B)仍然只是“一个术语”。
在Prolog中,除了预定义的术语(如:-、运算符等)之外,程序员定义的术语的语义取决于程序员决定的内容以及它在程序中使用的上下文(查询?断言?另一个术语的一部分?)。
在Prolog中,point(X, Y)是一个具有两个参数(point/2)的项,除了程序员决定并在程序中使用它之外,没有语义含义(不“做”任何事情)。如果我在Prolog提示符下输入point(X, Y),则如下:
?- point(1, 2).

Prolog将其视为一个查询,并尝试查找与point(1, 2)匹配且允许其成功的事实或规则。但是,如果我输入:

?- foo(point(1, 2)).

Prolog 只是看到一个针对 foo/1 的查询,寻找符合 foo(_)point(1, 2) 的事实或规则,除非有一个谓词子句将其放在某种上下文中进行进一步解释。例如,如果在我的数据库中针对 foo/1 我只有一个事实 foo(a). 并且没有 foo/1 的规则,那么 foo(point(1, 2)). 将会 失败 ,因为 Prolog 将尝试将参数为零的术语 a 与参数为 2 的术语 point(1, 2) 进行匹配并失败,并且没有其他可供尝试的选择。如果我有一个类似于这样的 foo/1 子句:

foo(X) :-
    X = point(A,B),
    ...   % do some things involving A and B

然后查询foo(point(1,2))将通过将Xpoint(1,2)相一致来匹配该条子句的头部,然后该条子句的第一行将统一point(1, 2) = point(A, B)并统一A = 1B = 2等。在这种情况下,point/2不会以任何方式被调用执行

假设我有以下foo/1子句:

foo(X) :-
    call(X),
    ...

现在如果我查询foo(point(1, 2)).foo/1将尝试调用point(1, 2)(将其作为查询执行),而Prolog将尝试查找与point(1, 2)匹配的事实或规则。

我不明白的是如何让point(X, Y)成为line的参数!

记住,这只是一个术语。除非在特定的上下文中使用,否则它没有语义。在Prolog中,除非以某种方式使用术语,否则它没有任何意义,除了程序员的思想之外。程序员可以决定定义一个术语line(A, B),表示从点A到点B的线条。如果我们有一个术语来定义两个坐标上的点,point(X, Y),我们还可以说line(point(X1, Y1), point(X2, Y2))。根据程序员的约定,这意味着我有一条从(X1, Y1)到(X2, Y2)的线

直到现在,复杂的术语仅表示实体是否相关。

我不确定你为什么说“直到现在”。用户决定该关系是什么,以及如何组织(复杂的)术语来表示该关系。当我们说line(point(X1, Y1), point(X2, Y2))时,这可以(根据程序员的自由裁量)表示从点(X1, Y1)(X2, Y2)的线条。

我能理解它是一个“传统函数”,如果实体相关则返回true或false。

这是不正确的。该术语不形成返回truefalse的“传统函数”。它只是一个术语,不返回任何东西。在Prolog中,正如我所提到的,术语的行为取决于上下文。术语可以成为一个查询,这意味着它被“调用”,而Prolog将尝试确定(通过先前断言的事实和规则/谓词),它是否可证明或真实。在这种情况下,它正在匹配底层术语函数名的事实或规则。因此,如果您实际上进行了查询line(point(X1, Y1), point(X2, Y2)),Prolog会查找与line(_, _)匹配的事实或规则并从那里开始。然后,它将根据是否能够匹配事实或成功完成规则来成功或失败。
但是如何将这作为线的参数呢?从理论上讲,“线”有两个参数(实体),并且它会说明它们是否相关。 按照程序员的约定,line(point(X,Y), point(X,Z))表示从点(X, Y)到点(X, Z)的一条直线。
那么,这些参数的值是真还是假? 不,这些参数根本没有任何“值”。它们只是定义了程序员选择要表示的某些术语或结构。
我可以理解“point(X,Y)”正在创建一个“point对象”。 它不会创建一个对象。它只是一个术语,表示横坐标为X,纵坐标为Y的点。
因此,线的参数是一个“点实体”。 line/2有两个参数,表示定义一条线的两个不同点(按照程序员的约定)。如果它被表达为line(P1, P2),那么点的“形式”未指定(程序员可以选择用列表表示点,例如[X,Y],或者像这种情况一样,使用用户定义的术语point(X,Y))。
但这不是我迄今为止对复杂术语的理解,所以我想要一个技术性的定义,解释嵌套的情况。 你到目前为止了解到哪些关于复杂术语的知识?为了帮助你理解,我们需要知道你读过什么让你感到困惑的东西。
如何在Prolog中定义一个有效的点?我可以使用术语point(X, Y)来定义它。但是XY是什么?它们如何被定义?我可能会强制要求它们是数字。因此,这里有一个规则来定义有效的point
valid_point(P) :-
    P = point(X, Y),   % a Point looks like point(X, Y)
    number(X),
    number(Y).

在Prolog中,我可以简化这个过程,因为我可以在一个子句的头部使用复杂的术语:
valid_point(point(X, Y)) :-
    number(X),
    number(Y).

因此,只有当 X 和 Y 都是数字时,valid_point(point(X, Y)) 才成功。如果你问Prolog是否 (point(3, 5.2) 是一个有效的点(查询 valid_point(point(3, 5.2)).),它会成功(输出 "true")。如果你问Prolog是否 point(a, 3) 是一个点(查询 valid_point(point(a, 3)).),它会失败(输出 "false")。
现在让我们定义一条线。一条线由任意两个点定义,所以我们可以将其表示为术语 line(P1, P2),其中 P1 和 P2 是有效且不相同的点。因此,我们可以如下定义有效的线。我将详细说明这一点,以便了解如何统一和使用术语:
valid_line(Line) :-
    Line = line(P1, P2),
    P1 = point(X1, Y1),    % P1 is a valid point
    valid_point(P1),    
    P2 = point(X2, Y2),    % P2 is a valid point
    valid_point(P2),
    ( X1 =\= X2 ; Y1 =\= Y2 ).

再次,Prolog允许我使用复合术语来进一步简化这个问题:

valid_line(line(point(X1, Y1), point(X2, Y2))) :-
    valid_point(point(X1, Y1)),
    valid_point(point(X2, Y2),
    ( X1 =\= X2 ; Y1 =\= Y2 ).

这个规则的意思是,如果point(X1, Y1)point(X2, Y2)都是有效的点,并且X1X2不相等,或者Y1Y2不相等,则它们形成一条有效线。

接下来我们讲一个更高级别的规则。如果一条线是有效的并且其点有相同的横坐标,则该线是垂直的。我们可以创建一个规则,名为vertical_line,如果线条参数是垂直的,则成功,否则失败:

vertical_line(line(point(X1, Y1), point(X2, Y2)) :-
    valid_line(line(point(X1, Y1), point(X2, Y2)),
    X1 = X2.

我们可以通过在子句头部统一横坐标来缩写它:
vertical_line(line(point(X, Y1), point(X, Y2))) :-
    valid_line(line(point(X, Y1), point(X, Y2)).

在上述所有示例中,我将规则名称与数据结构名称分开。因此,我有一个名为valid_line/1的规则,但是line/2表示一条线的结构。它们并不一定非要分开,但如果分开可能有助于避免混淆。即使它们相同,在Prolog中是否将其作为查询执行将取决于上下文。例如,我可以定义一个仅在参数为数字时成功的规则point/2
point(X, Y) :-
    number(X),
    number(Y).

那么我可以查询:

?- point(1, 3).
true

?- point(a, 7).
false.

然而,如果我接下来定义了line/2

line(P1, P2) :-
    P1 = point(X1, Y1),
    P2 = point(X2, Y2),
    ( X1 =\= X2 ; Y1 =\= Y2 ).

在进行一致性匹配 P1 = point(X1, Y1) 时,这不会强制执行有效的点(不调用 point/2)。这是因为Prolog中的谓词不是函数,不会表现出函数的行为。如果我查询 line(point(a, 3), point(c, d)),它可能会生成错误,因为我试图使用 (=\=)/2 数值比较坐标。在Prolog中,表达式 P1 = point(X1, Y1) 实际上是术语 '='(P1, point(X1, Y1))。正如我上面提到的,当Prolog进行调用时,它是在顶层术语上进行的,即在本例中是 '=',而 point(X1, Y1) 只是一个术语,不在此上下文中被调用。但是我可以按照以下方式调用 point/2

line(P1, P2) :-
    P1 = point(X1, Y1),
    call(P1),
    P2 = point(X2, Y2),
    call(P2),
    ( X1 =\= X2 ; Y1 =\= Y2 ).

然后Prolog会更优雅地检查点的有效性(根据我定义的point/2谓词)。但我认为这并不像单独定义valid_...谓词那样清晰。


1
哇,真的是详细的回答。你解答了我的疑惑,还讲得更多!基本上我需要知道的是“一个术语的行为确实取决于上下文”。而你也在最后用完美的例子解释了它。非常感谢! - Gonzalo Solera

4
我知道你说的是一个复杂术语,它表示n个实体之间的关系是否成立。在理论上,“line”有两个参数(实体),并且它会表示它们是否相关。
不幸的是,你对“复杂术语”的解释是错误的。Prolog使用这样的术语来表示“程序”和“数据”,因此一个术语的解释取决于它出现的上下文和它的用途。
考虑你的第一个例子:
love(john, sarah).

从句末的标点符号(以及您在知识库上下文中提到此内容的事实),我们知道这必须出现在Prolog文件的顶层结构中。这些术语定义了谓词子句。这些就是“程序”:该术语定义了谓词love/2/2是一个项的元数,即参数个数)的一个子句。我们可以通过“调用”它来将其用作“程序”:

?- love(X, Y).
X = john,
Y = sarah.

在这个“程序”上下文中,我们可以真正地说love/2是两个术语之间的关系。
但在你的第二个例子中,line这个术语并没有出现在顶层。它嵌套在一个vertical/1的术语中:
vertical(line(point(X,Y),point(X,Z))).

这意味着正在定义的“程序”是一元关系vertical/1,它作用于术语。更自然的叫法是“一组术语”。在这个例子中,“垂直线的集合是所有两个点具有相同X坐标的线的集合”。line/2不被定义为一个关系,它只是未解释的数据。我们可以称vertical/1
?- vertical(T).
T = line(point(_G921, _G922), point(_G921, _G925)).

但不是 line/2

?- line(P1, P2).
ERROR: toplevel: Undefined procedure: line/2 (DWIM could not correct goal)
摘要:只有“顶层”术语定义谓词,只有这些术语可以被必然解释为关系。在程序中出现的其他术语仅仅是关系所涉及的“数据”。

非常感谢!我不知道只有顶级术语定义了谓词。我现在想我理解了! - Gonzalo Solera

0
在编程术语中,变量按名称是“唯一的”。因此,“事实”
vertical(line(point(X,Y),point(X,Z))).

这个属性陈述了一个未解释术语(line(P1,P2))的特性,仅基于P1、P2的横坐标的相等性

现在,如果这有任何意义,它与特定的应用领域有关,但我们对此一无所知,除了平凡的几何直觉...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接