Prolog - 与递归中的打印顺序混淆

时间:2017-02-28 22:14:17

标签: prolog

鉴于以下计划:

postIt([]).
postIt([c|R]) :- postIt(R), !, nl.
postIt([X|R]) :- postIt(R), write(X).

查询:

?- postIt([a,b,c,d,e]).

跟踪:

    [trace]  ?- 
|    postIt([a,b,c,d,e]).
Call: (8) postIt([a, b, c, d, e]) ? creep
Call: (9) postIt([b, c, d, e]) ? creep
Call: (10) postIt([c, d, e]) ? creep
Call: (11) postIt([d, e]) ? creep
Call: (12) postIt([e]) ? creep
Call: (13) postIt([]) ? creep
Exit: (13) postIt([]) ? creep
Call: (13) write(e) ? creep
e
Exit: (13) write(e) ? creep
Exit: (12) postIt([e]) ? creep
Call: (12) write(d) ? creep
d
Exit: (12) write(d) ? creep
Exit: (11) postIt([d, e]) ? creep
Call: (11) nl ? creep

Exit: (11) nl ? creep
Exit: (10) postIt([c, d, e]) ? creep
Call: (10) write(b) ? creep
b
Exit: (10) write(b) ? creep
Exit: (9) postIt([b, c, d, e]) ? creep
Call: (9) write(a) ? creep
a
Exit: (9) write(a) ? creep
Exit: (8) postIt([a, b, c, d, e]) ? creep
true.

我不太明白为什么要输出' ' BA&#39 ;.我认为这个程序会打印出来的' ab'跳过' c'然后打印' de'我希望有人能够为我澄清这一点。

1 个答案:

答案 0 :(得分:3)

在我解决实际问题之前,还有一点:请考虑使用more_readable_names_with_underscores insteadOfStickingItAllTogetherLikeThis。因此,我建议使用名称post_it/1

接下来,为了让我们的生活更简单,我只会考虑您的代码的以下片段

post_it([]).
post_it([X|R]) :- post_it(R), write(X).

也就是说,我完全只是省略了第二个句子。

您对查询?- post_it([a,b,c,d]).的期望是什么?是abcd吗?

,显然不是

?- post_it([a,b,c,d]).
dcba

<强>为什么?在您的情况下,由于您使用的是不纯谓词write/1,因此只能理解程序性,这是考虑到实际 Prolog的执行策略

Prolog的执行策略被称为深度优先搜索与时间顺序回溯。如果您有以下条款:

a :- b, c.

然后,当执行a/0时,b/0将被调用第一个,而仅在b/0成功时调用{{1}调用。

因此,请考虑更简单的查询:

?- post_it([a,b]).

首先,c/0被调用,它会发出post_it([b])练习:为什么?)。仅发出然后b,因为awrite(a)中作为第二目标出现。

显然,在这种简单的情况下,我们仍然可以以某种方式掌握程序定义。这方面的主要缺点是,这很快变得太复杂无法理解,因此我只能建议避免副作用:它们总是会让你的代码变得太难了解,你已经看到了这个的第一个迹象。

建议:在Prolog的子集中工作。在您的情况下,请考虑列表之间的以下关系

without_c(Ls0, Ls) :-
        tfilter(dif(c), Ls0, Ls).

这使用来自Ulrich Neumerkel library(reif)post_it/1来声明性地描述两个列表之间的关系,其中第二个列表与第一个列表相同,但没有出现任何原子tfilter/3

示例查询:

?- without_c([a,b,c,d], Ls).
Ls = [a, b, d].

更多一般案例也有用,例如:

?- length(Ls0, _), without_c(Ls0, Ls).
Ls0 = Ls, Ls = [] ;
Ls0 = [c],
Ls = [] ;
Ls0 = Ls, Ls = [_7366],
dif(_7366, c) ;
Ls0 = [c, c],
Ls = [] .

请注意,这样的关系可以很容易测试,因为可以明确地推断出参数,这与仅在终端上发生的输出形成鲜明对比。此外,该程序还允许您进行声明性阅读,使您免于许多操作方面的考虑。

还要注意原始定义的另一个问题:由于使用了c,因此很难预测删除第二个子句的效果。生成的程序不是更具体(正如预期的那样),但实际上会发出以前未发出的解决方案!