鉴于以下计划:
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'我希望有人能够为我澄清这一点。
答案 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
,因为a
在write(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
,因此很难预测删除第二个子句的效果。生成的程序不是更具体(正如预期的那样),但实际上会发出以前未发出的解决方案!