【问题标题】:Concept questions about complex terms in PrologProlog中关于复杂术语的概念问题
【发布时间】:2017-03-11 10:59:42
【问题描述】:

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

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

love(john, sarah).

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

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

vertical(line(point(X,Y),point(X,Z))).

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

我可以理解“point(X,Y)”正在创建一个对象“point”。所以 line 的论点是一个“点实体”。但这不是我迄今为止所读到的关于复杂术语的内容,所以我喜欢一个解释嵌套案例的技术定义。

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

谢谢!

【问题讨论】:

    标签: prolog


    【解决方案1】:

    这是一个很好的问题,因为对于许多具有命令式语言背景的 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 为某些术语函子分配了特殊含义,并且@987654329 @ 在这种情况下用于谓词子句定义。但如果没有这个上下文,':-'(A, B) 仍然“只是一个术语”。

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

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

    在 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 (零参数的术语)与术语 point(1, 2Y)(具有 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 的论点呢?理论上,“线”有 2 个参数(实体),它会说明它们是否相关。

    line(point(X,Y), point(X,Z)) 是一个术语,按照程序员的约定,它表示从点 (X, Y) 到点 (X, Z) 的一条线。

    现在,参数的值是真还是假?

    不,参数根本没有任何。它们只是定义程序员选择代表的东西的术语或结构。

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

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

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

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

    但这不是我迄今为止所读到的关于复杂术语的内容,所以我喜欢一个解释嵌套案例的技术定义。

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


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

    如何在 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).
    

    所以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 ).
    

    再次,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,如果 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 进行数字比较。表达式 P1 = point(X1, Y1) 实际上是 Prolog 中的术语 '='(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_... 谓词那样清楚。

    【讨论】:

    • 哇,答案真的很详细。你已经回答了我的疑惑等等​​!基本上我需要知道的是“一个术语的行为确实取决于上下文”。最后你还用一个完美的例子来解释它。非常感谢!
    【解决方案2】:

    我理解一个复杂的术语表示 n 个实体之间的关系是真还是假 [...] “线”在理论上有 2 个参数(实体),它会说明它们是否相关。

    很遗憾,您对“复杂术语”的解释是错误的。 Prolog 使用这些术语来表示“程序”和“数据”,因此对术语的解释取决于它出现的上下文以及它的使用方式。

    考虑你的第一个例子:

    love(john, sarah).
    

    从最后这段时间开始(以及您在知识库的上下文中提到这一点的事实),我们知道这必须作为顶级结构出现在 Prolog 文件中。这些术语定义谓词的从句。这些是“程序”:这个术语定义(一个子句)谓词love/2/2 是一个术语的参数的常用 Prolog 符号,即参数的数量)。我们可以通过“调用”它来将其用作“程序”:

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

    总结:只有“顶级”术语才能定义谓词,并且只有这些术语才能必然被解释为关系。程序中出现的其他术语只是关系所谈论的“数据”。

    【讨论】:

    • 非常感谢!我不知道只有顶级术语定义谓词。我想我现在明白了!
    【解决方案3】:

    在一个术语中,变量在名称上是唯一的。所以,事实

    vertical(line(point(X,Y),point(X,Z))).
    

    仅基于 P1,P2 的横坐标的身份,声明未解释项的属性(line(P1,P2))。

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

    【讨论】:

      猜你喜欢
      • 2011-02-28
      • 2021-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多