【问题标题】:Need Help Understanding Somethings In Prolog需要帮助了解 Prolog 中的内容
【发布时间】:2011-04-28 09:58:03
【问题描述】:

我是 Prolog 的初学者,发现在使用规则时很难理解回溯的工作原理。我什至不知道回溯是否适用于规则(想正确了解它)。

我有以下程序将列表中的所有偶数相加。我自己写的,但很难理解找到解决方案所需的步骤。

evenN(X):- (X mod 2) =:= 0.

sumEven([], 0).
sumEven([H|T], X):- evenN(H), sumEven(T,Y), X is Y+H.
sumEven([H|T], X):- \+evenN(H), sumEven(T,X).

输出:::

?- sumEven([1,2,3,4,5,6],X).
X = 12

需要帮助以更好地理解它。我尝试使用跟踪实用程序来理解输出,但我不明白,这就是为什么在这里问它。

问题:

1)

当我注释掉第二条规则(最后一行)时,它给了我失败的答案,因为 1 不是偶数,并且整个 sumEven() 失败,因为我理解 evenN() 失败。我的问题是:之后会发生什么?它会回到顶部并尝试 sumEven([], 0) 事实还是?我只是想知道会发生什么。

2)

当包含最后一行(第二条规则)并且第一条规则失败时,当它回溯时,它是否会寻找跟随它的另一个 sumEven() (就像第二条规则遵循失败的第一条规则的方式)或它回到顶部并测试 sumEven([], 0) 事实并从那里开始?

我需要了解在 prolog 中使用规则时它是如何回溯的,特别是在像这样的递归情况下。

3) 我在网上找到了以下代码(递归)。它将列表分为正面列表和负面列表。

% predicates
    split(list,list,list)

% clauses
    split([],[],[]).
    split([X|L],[X|L1],L2):-
        X>= 0,
        !,    
        split(L,L1,L2).

    split([X|L],L1,[X|L2]):-
        split(L,L1,L2).

Output :        

Goal: split([1,2,-3,4,-5,2],X,Y)

X=[1,2,4,2], Y=[-3,-5]

有人可以帮助我了解找到解决方案的工作方式吗?我的意思是我想逐步了解它是如何执行以提出解决方案的。

【问题讨论】:

    标签: prolog


    【解决方案1】:

    匹配子句按照它们在程序中出现的顺序进行尝试。在好的声明式 Prolog 程序中,子句的顺序不会改变程序的含义。这接近逻辑,其中析取是可交换的。 sumEven/2 有这个属性。不过,它的名字非常错误,因为 sum 是关系的第二个参数,而不是第一个参数。一个更好的名字会是例如 even_sum/2,你可能会想出更好的名字。 split/3 使用 !/0,它破坏了这个属性并让子句的顺序很重要。出于这个原因,本地 if-then-else ((->)/2 和 (;)/2) 似乎更适合这种情况。使用带有?- gtrace, your_goal. 的 SWI-Prolog 的图形跟踪器代替 trace/0,它还向您显示在哪些点上还有待尝试的替代方案。一般来说,从关系的角度思考并问:这个从句什么时候成立?它成立 if ... 等。这使得推理更大的程序成为可能,其中确切的执行流程可能更难理解。

    【讨论】:

    • 感谢您的回复。我的第三个问题还没有回答
    【解决方案2】:

    看看你是否能掌握 Ivan Bratko 的“Prolog Programming for AI”。

    他解释了 Prolog 为很好地满足目标而遵循的过程。

    我将尝试逐个回答您的问题:

    clause 1: evenN(X):- (X mod 2) =:= 0.
    clause 2: sumEven([], 0).
    clause 3: sumEven([H|T], X):- evenN(H), sumEven(T,Y), X is Y+H.
    clause 4: sumEven([H|T], X):- \+evenN(H), sumEven(T,X).
    

    问题:

    1) 当我注释掉第二条规则(最后一行)时,它给了我失败的答案,因为 1 不是偶数,并且整个 sumEven() 失败,因为我理解 evenN() 失败。我的问题是:之后会发生什么?它会回到顶部并尝试 sumEven([], 0) 事实还是?我只是想知道会发生什么。

    答:您注释掉第 4 条。Prolog 尝试满足目标,首先尝试第 2 条,但由于列表不为空而失败,然后尝试第 3 条,但在规则上失败1 当 H 为奇数时。现在它将尝试在第 3 条之后找到另一个子句(它不会回溯到第 3 条之前),由于您将其注释掉,该子句将失败,因此目标失败。

    2)当最后一行(第二条规则)被包含,并且第一条规则失败时,当它回溯时,它是否会寻找跟随它的另一个 sumEven() (就像第二条规则遵循失败的第一条规则的方式一样) 还是返回顶部并测试 sumEven([], 0) 事实并从那里开始?

    答:不,当第 3 条失败时它不会回溯,而是继续下一条第 (4) 条。

    我需要了解在 prolog 中使用规则时它是如何回溯的,特别是在像这样的递归情况下。

    A:如果规则 evenN(H) 成功,sumEven(T, Y) 使用 T 和 Y 从第 2 条再次开始整个过程​​。如果 sumEven(T,Y) 失败了一些原因,第 3 条将失败,Prolog 回溯并尝试第 4 条。如果当前调用是 sumEven([2,3,...],X) 并且 sumEven([3,...],Y) 由于某种原因失败, Prolog 将回溯并尝试在第 3 条之后找到另一个子句 sumEven([2,3,...],X)。

    3) 我在网上找到了以下代码(递归)。它将列表分为肯定列表和否定列表。

    clause 1:    split([],[],[]).
    clause 2:    split([X|L],[X|L1],L2):- X>= 0, !, split(L,L1,L2).
    clause 3:    split([X|L],L1,[X|L2]):- split(L,L1,L2).
    

    有人可以帮助我了解找到解决方案的工作方式吗?我的意思是我想逐步了解它是如何执行以提出解决方案的。

    A:我将使用较短的目标拆分(numlist,PosList,NegList),其中 numlist = [1,-1,2,-2]。非常粗略地,它的工作原理如下(它实际上使用一个堆栈来放置匹配的值,并且仅在它展开此堆栈时目标最终成功时才实例化变量 - 有关详细信息,请参阅 Bratko 书):

    第 1 条失败,因为 numlist 不为空。

    第 2 条适用于: split([1|-1,2,-2],[1|L1],[Y|L2]) - 因为 1 >=0 PosList 现在将是 [1],并且split 将应用于 numlist=[-1,2,-2] 的尾部。

    第 1 条再次失败。第 2 条适用于 split([-1|2,-2],[-1| L1],[Y|L2]) - 由于 -1

    第 1 条失败;第2条成功,PosList变为[1,2],对numlist=[-2]应用split。

    第 1 条和第 2 条失败;第 3 条成功,NegList 变为 [-1,-2]。 numlist尾部为空,第1条成功,返回PosList=[1,2]和NegList[-1,-2]。

    【讨论】:

    • @Kap:我有第一版。它就足够了,因为 Prolog 是相当静态的,我想任何 ed。会做。 (P.S:它也有一个非常简洁的 prolog 引擎算法 +/- 20 行伪代码)
    • 非常感谢您花时间回答。我很感激。
    猜你喜欢
    • 1970-01-01
    • 2017-06-19
    • 2016-05-03
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多