关于 Prolog 中复杂术语的概念问题

Gon*_*era 6 prolog

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

我想内化这种语言的“哲学”,所以我想非常准确地理解复杂术语(或复合术语)是什么。到目前为止,我已经读到复杂的术语是函子并且它是元数。我可以使用这样的复杂术语构建知识库:

love(john, sarah).
Run Code Online (Sandbox Code Playgroud)

假设现在我可以输入任何可能的字符串对作为“love”的参数,除非该字符串对是“john,sarah”,否则它将为假。是的,所以函子及其参数的“问题”是对还是错,取决于我是否在我的知识库中明确说过这一点,或者 Prolog 是否可以使用规则、统一等从他拥有的信息中推断出它。

是的,从这里开始,我明白一个复杂的术语表示 n 个实体之间的关系是true 或 false。我不明白的是:

vertical(line(point(X,Y),point(X,Z))).
Run Code Online (Sandbox Code Playgroud)

我明白什么是(X,Y)。做。也就是说,任何实体都通过“点”与任何其他实体相关。我不明白的是点(X,Y)如何可以作为线的参数!到目前为止,一个复杂的术语只是表示实体是否相关。我可以理解这是一个“传统函数”,如果实体相关则返回 true 或 false。但这怎么能成为 line 的论证呢?理论上,“line”有 2 个参数(实体),它会说明它们是否相关。现在,参数的值是 true 还是 false?

我可以理解“点(X,Y)”正在创建一个对象“点”。所以线的参数是一个“点实体”。但这不是我到目前为止所读到的有关复杂术语的内容,所以我希望有一个技术定义来解释嵌套的情况。

(如果我使用了错误的术语或定义,我很抱歉,我是 Prolog 新手)

谢谢!

lur*_*ker 5

这是一个很好的问题,因为对于许多来自命令式语言背景的 Prolog 初学者来说,这是一个基本的困惑点。我在学习 Prolog 时的建议通常是“忘记(几乎)你所学到的有关编程的所有内容,从基础开始”。

Prolog 是建立在可以有零个或多个参数的项之上的。foo是一个没有参数的术语。foo(A, B)是一个有两个参数的项。foo(bar(X), bah(Y,Z))是一个有两个参数的复杂项(称为),其参数由项(有一个参数)和(有两个参数)foo/2组成。bar/1bah/2

甚至谓语从句也是以下形式Head :- Body或规范形式的术语:':-'(Head, Body)。当这种术语在 Prolog 程序中断言(在文件中静态声明,甚至动态断言)时,Prolog 将其识别为谓词,因为Prolog 为某些术语函子分配了特殊含义,:-在本例中是用于谓词子句定义。但如果没有这个背景,':-'(A, B)它仍然“只是一个术语”。

在 Prolog 中,除了预定义的术语(例如:-、运算符等)之外,程序员定义的术语的语义仅取决于程序员的决定以及该术语所处的上下文(查询?断言?另一个术语的一部分?)在他们的程序中使用。

我明白什么是(X,Y)。做。也就是说,任何实体都通过“点”与任何其他实体相关。

在 Prolog 中,point(X, Y)是一个带有两个参数 ( ) 的术语point/2,它没有语义意义(不“做”任何事情),除非程序员决定然后在程序中使用它。如果我point(X, Y)在 Prolog 提示符下输入如下:

?- point(1, 2).
Run Code Online (Sandbox Code Playgroud)

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

?- foo(point(1, 2)).
Run Code Online (Sandbox Code Playgroud)

Prolog 只是看到一个关于foo/1寻找匹配的事实或规则的查询foo(_),并且point(1, 2)“只是一个术语”,没有进一步的解释,除非有一个谓词子句将其放入这样做的上下文中。例如,如果 for我的数据库中foo/1只有一个事实并且 没有规则,将会失败,因为 Prolog 会尝试将术语(具有零个参数的术语)与术语(具有 2 个参数的术语)相匹配并且会失败,并且没有其他选择可以尝试。如果我有这样的条款:foo(a).foo/1foo(point(1, 2)).apoint(1, 2)foo/1

foo(X) :-
    X = point(A,B),
    ...   % do some things involving A and B
Run Code Online (Sandbox Code Playgroud)

然后查询将通过 unify withfoo(point(1,2))来匹配该子句的头部,然后该子句的第一行将 unify并 unify and等等。在此上下文中不会以任何方式调用执行Xpoint(1,2)point(1, 2) = point(A, B)A = 1B = 2point/2

假设我有以下foo/1条款:

foo(X) :-
    call(X),
    ...
Run Code Online (Sandbox Code Playgroud)

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

我不明白的是点(X,Y)如何可以作为线的参数!

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

到目前为止,一个复杂的术语只是表示实体是否相关。

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

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

这不是真的。该术语不形成返回 true或 的“传统函数” false。它只是一个术语,不会返回任何内容。正如我所提到的,在 Prolog 中,术语的行为确实取决于上下文。术语可以是一个查询,这意味着它被称为,并且 Prolog 将尝试确定(通过先前断言的事实和规则/谓词)它是可证明的或真实的。在这种情况下,它将事实或规则与顶级术语函子名称相匹配。因此,如果您实际查询 line(point(X1, Y1), point(X2, Y2)),Prolog 会查找事实或匹配的规则line(_, _),然后从那里开始。然后,它会成功或失败,具体取决于它是否可以匹配事实或成功完成规则。

但这怎么能成为 line 的论证呢?理论上,“line”有 2 个参数(实体),它会说明它们是否相关。

line(point(X,Y), point(X,Z))按照程序员的惯例,这个术语表示从点到点的一条(X, Y)线(X, Z)

现在,参数的值是 true 还是 false?

不,这些争论根本没有任何价值。它们只是定义程序员选择表示的事物的术语或结构。

我可以理解“点(X,Y)”正在创建一个对象“点”。

它不创建对象。它只是一个表示横坐标X和纵坐标点的术语Y

所以线的参数是一个“点实体”。

line/2有两个参数,它们代表定义一条线的两个不同点(按照程序员的约定)。如果表示为,line(P1, P2)则未指定点的“形式”(程序员可以选择用列表来表示点,例如[X, Y],或者在本例中是用户定义的术语 )point(X, Y)

但这不是我到目前为止所读到的有关复杂术语的内容,所以我希望有一个技术定义来解释嵌套的情况。

到目前为止,您读到过哪些关于复杂术语的内容?为了帮助解决这个问题,我们需要知道您读到的哪些内容让您感到困惑。


您尚未显示任何上下文代码,因此让我们构建一些上下文来帮助理解这一点。我将选择定义一个看起来像“point(X, Y)”的点,以及一条看起来像“line(P1, P2)”的线,其中“P1”和“P2”是点。因此,我也可以将一条线表示为“line(point(X1, Y1), point(X2, Y2))”。这是我作为一个程序员的选择。

我如何在 Prolog 中定义有效点?我可以用术语来定义它point(X, Y)。但是X和是什么Y?如何定义它们?我可能会强制要求它们是数字。所以这是一个有效的规则point

valid_point(P) :-
    P = point(X, Y),   % a Point looks like point(X, Y)
    number(X),
    number(Y).
Run Code Online (Sandbox Code Playgroud)

在 Prolog 中,我可以稍微简化一下,因为我可以使用一个复杂的术语来表示子句的开头:

valid_point(point(X, Y)) :-
    number(X),
    number(Y).
Run Code Online (Sandbox Code Playgroud)

因此valid_point(point(X, Y)),只有当XY都是数字时才会成功。如果你问 Prolog 是否(point(3, 5.2)是一个有效点(查询 )valid_point(point(3, 5.2)).,它会成功(说“真”)。如果你问 Prolog 是否point(a, 3)是一个点(查询 )valid_point(point(a, 3)).,它会失败(说“假”)。

现在让我们定义一条线。一条线由任意两点定义,因此我们可以将其表示为术语,line(P1, P2)其中P1P2是不相同的有效点。因此,我们可以如下定义一条有效线。我将详细地展示这一点,看看如何统一和使用术语:

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 ).
Run Code Online (Sandbox Code Playgroud)

同样,Prolog 允许我使用复合术语来进一步简化:

valid_line(line(point(X1, Y1), point(X2, Y2))) :-
    valid_point(point(X1, Y1)),
    valid_point(point(X2, Y2),
    ( X1 =\= X2 ; Y1 =\= Y2 ).
Run Code Online (Sandbox Code Playgroud)

该规则规定,如果point(X1, Y1)point(X2, Y2)是有效点,并且X1X2不相等,或者Y1Y2不相等,则 和 形成一条有效线。

让我们继续讨论更高级别的规则。如果一条线有效并且其点具有相同的横坐标,则该线是垂直的。我们可以创建一个规则,vertical_line如果 line 参数是垂直的,则该规则成功,否则失败:

vertical_line(line(point(X1, Y1), point(X2, Y2)) :-
    valid_line(line(point(X1, Y1), point(X2, Y2)),
    X1 = X2.
Run Code Online (Sandbox Code Playgroud)

我们可以通过统一子句头部的横坐标来缩写它:

vertical_line(line(point(X, Y1), point(X, Y2))) :-
    valid_line(line(point(X, Y1), point(X, Y2)).
Run Code Online (Sandbox Code Playgroud)

在上述所有示例中,我将规则名称与数据结构名称分开。所以我valid_line/1通常有一个规则,但line/2代表一条线的结构。没有理由必须将它们分开,但如果分开的话可以帮助避免混乱。即使它们相同,Prolog 是否将其作为查询执行将取决于上下文。例如,我可以定义一个point/2仅当参数为数字时才成功的规则:

point(X, Y) :-
    number(X),
    number(Y).
Run Code Online (Sandbox Code Playgroud)

然后我可以查询:

?- point(1, 3).
true

?- point(a, 7).
false.
Run Code Online (Sandbox Code Playgroud)

但是,如果我然后定义line/2

line(P1, P2) :-
    P1 = point(X1, Y1),
    P2 = point(X2, Y2),
    ( X1 =\= X2 ; Y1 =\= Y2 ).
Run Code Online (Sandbox Code Playgroud)

在进行统一时,这不会强制执行有效点(不调用)。这是因为 Prolog 中的谓词不是函数,并且行为方式也不同。如果我要查询,它可能会产生错误,因为我试图将坐标与. 该表达式实际上是 Prolog 中的术语。正如我上面提到的,当 Prolog 进行调用时,它是在顶级术语上,在本例中是 ,并且 是“只是一个术语”,在这种情况下不会被调用。我可以按如下方式调用:point/2P1 = point(X1, Y1)line(point(a, 3), point(c, d))(=\=)/2P1 = point(X1, Y1)'='(P1, point(X1, Y1))'='point(X1, Y1)point/2

line(P1, P2) :-
    P1 = point(X1, Y1),
    call(P1),
    P2 = point(X2, Y2),
    call(P2),
    ( X1 =\= X2 ; Y1 =\= Y2 ).
Run Code Online (Sandbox Code Playgroud)

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