【问题标题】:Prolog Recursion and TerminationProlog递归和终止
【发布时间】:2012-02-16 20:20:37
【问题描述】:

第一个 StackOverflow 问题。

我在 prolog 中编写了一个带有三个参数的谓词。它们(分别)是一个字符,一个字符串列表,最后一个参数是第二个参数中以第一个参数开头的所有字符串的列表。我的写语句替代了我完全缺乏关于如何在 SWI-Prolog 中进行跟踪的知识。无论如何,进入代码!


startString(C, [H1|T1], [H2|T2]) :-
    atom_chars(H1, [C| _ ]),
    H2 = H1,
    startString(C, T1, T2).

startString(C, [ _ |T1], Y) :-
    startString(C, T1, Y),
    write(foo).

startString(_, [], []) :-
    write(foo).

哪些输出:


foofoofoo

X = [some, simple]

我的方法是正确的,但谓词没有终止(在写入 X 之后缺少句点并不是一个错误)。我的问题是,为什么不是?从我在互联网上找到的有限递归示例中,我的谓词的第三个版本应该终止谓词并使 x 成为明确的答案。

当我按下回车键时,我可以输入另一个查询,但是我在同一个程序中编写的另一个谓词中有同样的小“问题”。对这个谓词的任何帮助也应该传递给另一个。谢谢!

【问题讨论】:

    标签: recursion prolog


    【解决方案1】:

    要将您在 cmets 中的问题扩展到 @ScottHunter,

    我想我的问题是,为什么我的其他谓词“知道”他们有正确的答案,而这个和另外一个没有?

    答案与堆栈上是否存在选择点有关。考虑这种情况:

    reasonably_big(L, X) :- member(X, L), X > 100.
    
    ?- reasonably_big([105, 2], X).
    X = 105 ;
    false.
    ?-
    

    对比一下:

    ?- reasonably_big([2, 105], X).
    X = 105.
    ?-
    

    在第二种情况下,Prolog“知道”没有更多的解决方案;在第一种情况下它没有。这两种情况之间的区别在于,在第一种情况下,member 在堆栈上留下了一个选择点:列表中还有另一个项目可以考虑找到另一个答案。在第二种情况下,列表的其余部分是空的,并且 SWI-Prolog 的 member 足够聪明,不会在这种情况下在堆栈上留下选择点,因此它从不询问您是否需要其他解决方案。

    如果您得到无关的选择点,则通常表明存在逻辑错误。例如,考虑min的这个定义:

    min(X, Y, X) :- X =< Y.
    min(X, Y, Y).
    

    这是有缺陷的,因为你总是可以回溯并得到另一个值;即:

    ?- min(3,4, X).
    X = 3 ;
    X = 4.
    

    第二个解决方案是错误的。但是你仍然可以通过错误的改进得到一个不必要的选择点:

    min(X, Y, X) :- X =< Y.
    min(X, Y, Y) :- Y =< X.
    

    看看当我们尝试不同的值时会发生什么:

    ?- min(4,3,X).
    X = 3.
    ?-
    
    ?- min(3,4,X).
    X = 3 ;
    false.
    ?-
    

    第一个运行良好,但第二个通过拥有第二个主体在堆栈上留下了一个选择点。你可以用绿色切割来消除它:

    min(X,Y,X) :- X =< Y, !.
    min(X,Y,Y) :- Y =< X.
    
    ?- min(3,4,X).
    X = 3.
    ?-
    
    ?- min(4,3,X).
    X = 3.
    ?-
    

    剪辑将 Prolog 提交给特定的解决方案。它说,一旦你做到了,就没有必要为这个谓词尝试任何其他解决方案,因为我们已经找到了我们唯一想要的解决方案。了解如何应用剪辑是一个复杂的话题,但消除不受欢迎的选择点是一个重要用途。

    【讨论】:

    • 我找到了解决我的问题的方法,它与剪切有关。这是一个复杂的话题,我仍然不能完全确定我是否理解,但是将它添加到我的谓词版本的末尾允许 prolog 以我想要的方式“停止”。我认为这与递归和我选择解决这个问题的方法有很大关系。感谢您的深入回答,它将帮助我完成我的其他序言任务!
    【解决方案2】:

    您必须注意 Prolog 中“终止”的含义。当您要求 prolog 证明 startString('s',some-list,X) 时,您得到了一个绑定到 X 的值,这意味着它已终止(否则它仍会搜索 X 的值)。但在另一种意义上它没有,因为您可以询问(通常通过输入分号)Prolog 来尝试找到另一种解决方案,并且可以继续询问,直到它耗尽了这样做的可能性。听起来您的控制台正在等待您告诉它您是想尝试寻找另一个解决方案,还是您已经完成(当您按 Enter 时会发生这种情况,并且解释器允许您输入新查询)。

    【讨论】:

    • 听起来您的控制台正在等待您告诉它是否要尝试找到另一个解决方案我想我的问题是,为什么我的其他谓词“知道”他们有正确的答案,而这个和另外一个不?如何让解释器知道它成功了?
    • 你能给我一个“知道”它有正确答案的谓词的例子吗?与其说它不是正确答案,不如说它不是正确的答案;就口译员而言,它们都是正确的答案。您没有要求口译员找到另一个答案这一事实表明您不想要另一个答案。
    • 这是一个“正确”谓词的示例:'altElement([],[])。 altElement([H1|T1],[H2|T2]) :- H2 是 H1,removeElem(T1,[H2|T2])。删除元素([],[_])。 removeElem([_|T1],[_|T2]) :- altElement(T1,T2)。 ' 谓词接受一个列表,如果第二个参数是变量,则返回新列表中的第一个、第三个、第五个......等元素。
    • 如果您要求证明 altElement([1,2,3],X),它会返回 X = [1,5] 但不允许您要求替代解决方案?
    • 返回 X = [1,3]。然后终端准备好进行另一个谓词查询,显示 ?-
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多