【问题标题】:New to gprolog - how can I make this short program reach the correct conclusion?gprolog 的新手 - 我怎样才能让这个简短的程序得出正确的结论?
【发布时间】:2013-06-28 00:11:36
【问题描述】:

我有以下 gprolog 程序:

father(charles, harry). 
daughter(elizabeth, george).
son(william, charles). 
father(X, Y) :- son(Y, X).
father(X, Y) :- daughter(Y, X).
daughter(X, Y) :- \+son(X, Y), father(Y, X).
son(X, Y) :- \+daughter(X, Y), father(Y, X).

鉴于我所拥有的,我尝试了这个查询:

| ?- son(harry, charles).

...并得到了no(又名,据我了解,“Prolog 无法证明这一点。”)。当我对其进行跟踪时,它似乎并没有太大帮助:

{trace}
| ?- son(harry, charles).
      1    1  Call: son(harry,charles) ? 
      1    1  Fail: son(harry,charles) ? 

由于son(harry, charles) 应该与我的代码的最后一行匹配,我认为程序只需从检查第一部分开始:\+daughter(X, Y)(“Prolog 无法证明 Harry 是 Charles 的女儿”,正确的?)。确实:

| ?- \+daughter(harry, charles).
      1    1  Call: \+daughter(harry,charles) ? 
      2    2  Call: daughter(harry,charles) ? 
      2    2  Fail: daughter(harry,charles) ? 
      1    1  Exit: \+daughter(harry,charles) ? 

(1 ms) yes

那么它会进入第二部分,对吗?它将检查father(Y, X),即father(charles, harry),这是作为事实给出的!

| ?- father(charles, harry).
      1    1  Call: father(charles,harry) ? 
      1    1  Exit: father(charles,harry) ? 

(1 ms) yes

既然这两个都是真的,为什么不给yes 一个son(harry, charles)?作为一个重要的说明,这确实与不连续的谓词警告一起编译:

compiling /Users/nicolejulian/Dropbox/AI/test.pl for byte code...
/Users/nicolejulian/Dropbox/AI/test.pl:4: warning: discontiguous predicate father/2 - clause ignored
/Users/nicolejulian/Dropbox/AI/test.pl:5: warning: discontiguous predicate father/2 - clause ignored
/Users/nicolejulian/Dropbox/AI/test.pl:6: warning: discontiguous predicate daughter/2 - clause ignored
/Users/nicolejulian/Dropbox/AI/test.pl:7: warning: discontiguous predicate son/2 - clause ignored
/Users/nicolejulian/Dropbox/AI/test.pl compiled, 8 lines read - 617 bytes written, 11 ms

(1 ms) yes

早期的一些实验让我看起来基本上可以在警告和无限循环之间做出选择,所以我照原样接受了这个。 (例如,对所有左侧标签进行分组会删除警告,但会导致查询 | ?- father(elizabeth, george). 崩溃)。

无论如何,如果我不能只是“作弊”并添加son(harry, charles) 作为事实,那么我对 Prolog 的执行步骤有什么不了解的地方?

【问题讨论】:

    标签: prolog logic


    【解决方案1】:

    您显示的警告中的关键短语是clause ignored。所以你的父子逻辑,过去father(charles, harry) 的一切都被忽略了。尝试使它们连续:

    father(charles, harry). 
    father(X, Y) :- son(Y, X).
    father(X, Y) :- daughter(Y, X).
    
    daughter(elizabeth, george).
    daughter(X, Y) :- \+son(X, Y), father(Y, X).
    
    son(william, charles). 
    son(X, Y) :- \+daughter(X, Y), father(Y, X).
    

    然后您将得到 true 作为您查询的答案。

    但是,如果您在 true 之后键入 a(要求所有解决方案),则逻辑中存在进一步的问题。它没有优雅地结束,而是进入循环并溢出堆栈。这是由于父亲、儿子和女儿之间的循环逻辑。所以你可能想仔细考虑这部分。

    要绕过循环逻辑,请考虑this tutorial 中给出的示例。这个案例对person 使用断言事实(给出姓名、性别和每个父母,然后使用谓词来建立这些关系和其他关系,通过登陆事实来避免循环,而不是无限循环回到其他谓词。可能有其他类似的方法,但这只是一个例子。

    【讨论】:

    • 我的印象是带有表格的 Prolog(例如 YAP Prolog、Bprolog 等)可以处理这样的相互递归规则,但我自己从未尝试过。
    • “忽略”是什么意思?使用father(charles, harry) 之后列出的事实的其他查询似乎确实有效,所以它毕竟必须了解一些东西......?
    • @nicole 第 2 行和第 6 行分别是 daughter/2 的子句。警告告诉您它忽略了第 6 行的子句,因为它不在第 2 行的子句旁边。(反之亦然)。所以它忽略了其中一条线,只使用另一条线。
    • @nicole 您还可以告诉 SWI 不要将其视为指令问题。 :- discontiguous daughter/2 将指示它期望 daughter/2 的子句出现中间的东西。
    • @mbratch no 实际上我同意您在回答末尾所说的话,更好的方法(至少在“正常”Prolog 的限制内)是重组代码,以便不是循环递归。根据我的经验,添加削减通常会使问题变得更糟。 :) 如果可以使用once/1,语义更清晰,我更喜欢它。顺便说一句,有趣的是in the YAP documentation 表格示例就是这个问题。
    猜你喜欢
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    • 2019-11-10
    相关资源
    最近更新 更多