【问题标题】:Prolog and limitations of backtrackingProlog 和回溯的限制
【发布时间】:2016-03-26 12:34:30
【问题描述】:

这可能是在 Prolog 中返回列表长度的函数的最简单的实现

count([], 0).
count([_|B], T) :- count(B, U), T is U + 1.

关于 Prolog,我仍然无法理解的一件事是使用变量作为参数的灵活性。

例如,我可以运行count([a, b, c], 3). 并获得true。我也可以运行count([a, b], X). 并得到答案X = 2.。奇怪的是(至少对我来说)我还可以运行 count(X, 3). 并获得至少一个结果,在解释器消失到无限循环之前看起来类似于 X = [_G4337877, _G4337880, _G4337883] ;。我什至可以运行像count(X, A). 这样真正“灵活”的东西并得到X = [], A = 0 ; X = [_G4369400], A = 1.,这显然是不完整的,但在某种程度上真的很好。

因此我的问题是多方面的。我可以以某种方式向 Prolog 解释在执行count(X, 3). 时不要超越第一个结果吗?我可以让 Prolog 为count(X, A). 生成任意数量的解决方案吗?我可以生成什么样的解决方案有限制吗?这个特定的谓词是什么使我无法为所有可能的查询类型生成所有解决方案?

【问题讨论】:

    标签: prolog


    【解决方案1】:

    这可能是最简单的实现

    取决于观点:考虑

    count(L,C) :- length(L,C).
    

    更短更实用。这也适用于您的用例。

    编辑

    CLP(FD) 允许

    :- use_module(library(clpfd)).
    
    count([], 0).
    count([_|B], T) :- U #>= 0, T #= U + 1, count(B, U).
    
    ?- count(X,3).
    X = [_G2327, _G2498, _G2669] ;
    false.
    

    (进一步)回答 cmets

    明显是讽刺

    不,很抱歉给人留下这样的印象。这是试图为您的问题提供综合答案。 length/2 实现的每一个细节——实际上比你的代码长得多——都经过仔细权衡,为我们提供了一个通用且高效的构建块。

    一定有一些通用的概念

    我会称(完整)Prolog 这样的一般概念。从一开始,Prolog 就要求我们解决计算任务描述谓词参数之间的关系。一旦我们描述了我们的关系,我们就可以查询我们的“知识数据库”,Prolog 会尝试以特定的顺序枚举所有答案。

    unificationdepth first search(回溯)等高级概念是此模型的关键。

    现在,我认为您正在寻找像var/1 这样的二阶 构造,它允许我们推理关于 我们的谓词。这样的构造不能用(纯)Prolog 编写,并且不断增长的school of thinking 需要避免它们,因为它们很难使用。所以我发布了一个使用 CLP(FD) 的替代方案,它在某些情况下有效地保护了我们。在这个问题特定的上下文中,它实际上给了我们一个简单而优雅的解决方案。

    我不想重新实现长度

    好吧,我知道这一点,但是既然 count/2 aliases length/2,为什么不研究参考模型呢? (参见 SWI-Prolog 网站上的 source

    【讨论】:

    • @vasily:这真的是你想要的答案吗?
    • @CapelliC,对不起,但这对我来说非常神秘。我不是试图重新实现长度,我试图理解为什么我的原始和蓝眼睛的实现会失败,以及我的实现是什么使它“不完整”。必须有一些一般概念来描述我在这里处理的内容。
    • @CapelliC,感谢您的解释和纯度主题的链接。事实上,这个问题与我所问的非常相关:stackoverflow.com/questions/31941407/…
    • @vasily:我这样说没有任何讽刺或神圣的意味:老实说,这是我在 stackoverflow 上看到的关于 Prolog 的最快的学习曲线之一。为 CapelliC 和 @tas 的观察和高度合理的问题和有见地的答案 +1!
    • 一阶:参数变量。二阶:谓词变量。 var/1正如你所说:元逻辑
    【解决方案2】:

    查询count(X,3) 得到的答案实际上一点也不奇怪。您在询问哪些列表的长度为 3。您会得到一个包含 3 个元素的列表。出现无限循环是因为递归规则的第一个目标中的变量 B 和 U 未绑定。在那个目标之前你没有任何可能失败的东西。所以总是可以遵循递归。在 CapelliC 版本中,如果第二个参数小于 1,则在递归之前的第二条规则中有 2 个目标会失败。如果考虑这个稍微改变的版本,可能会变得更清楚:

    :- use_module(library(clpfd)).
    
    count([], 0).
    count([_|B], T) :-
        T #> 0,
        U #= T - 1,
        count(B, U).
    

    您的查询

    ?- count(X,3).
    

    不会匹配第一个规则,而是第二个规则,并继续递归直到第二个参数为 0。此时,第一个规则将匹配并产生结果:

    X = [_A,_B,_C] ?
    

    第二条规则的头部也会匹配,但它的第一个目标会失败,因为T=0

    X = [_A,_B,_C] ? ;
    no
    

    在您的上述版本中,由于未绑定变量 B 和 U,Prolog 将尝试第二条规则的递归目标,因此无限循环。

    【讨论】:

    • 这在没有 clpfd 的情况下也可以工作,不是吗?计数([],0)。 count([_|B], T) :- T > 0, U 是 T - 1, count(B, U).
    • @Bezewy:不,尝试最通用的查询?- count(List,Len). 它会在回溯时崩溃,应该枚举所有解决方案,就像 CLP(FD) 版本一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    相关资源
    最近更新 更多