修剪下面代码中的选择点如何使其更高效(Prolog)?
How does pruning choice points in the code below make it more efficient (Prolog)?
在下面给出的代码中,有!
(剪切)修剪选择点以提高效率。我很确定 reverse
谓词和 agent_do_moves
谓词是必不可少的。
solve_task(Task,Cost):-
agent_current_position(oscar,P),
solve_task_a(Task,[b(0,0,P)],[],R,Cost,_NewPos),!, % prune choice point for efficiency
reverse(R,[_Init|Path]),
agent_do_moves(oscar,Path).
上面例子中的剪切效果如下:
理想情况下,它 提交 可能在 solve_task_a/6
内发生的搜索到找到的第一个答案。这释放了资源以寻找进一步的答案,从而改善 space 消费。
范围问题
然而,与此同时,它也可能隐藏 agent_current_position/2
的进一步答案。当然,对于这个目标有进一步的答案意义不大,但有可能是一个错误,恰好休眠了一段时间,才变得活跃,但在最坏的情况下仍然没有被发现。
出于这个原因,最好写而不是剪切
...,
once( solve_task_a( ... ) ),
...
这将范围精确地限制为您要表达的内容。
踏实度问题
但这不是唯一可能的问题来源。我看到这个变量 Cost
。调用solve_task(Task, Cost)
时会实例化还是不实例化?我可以在这里做很多猜测。但至少这个变量可能会影响 Prolog 将提交的答案。所以 solve_task(Task, 99)
和 solve_task(Task, Cost), Cost = 99
可能会产生不同的答案。事实上,后者甚至可能会失败。据说有这样问题的谓词缺乏坚定性.
为了说明在这种情况下如何容易失去坚定,请考虑您的(已经改进的)程序的这个(可运行的)草图:
solve_task(Task,Cost):-
% agent_current_position(oscar,P),
once(solve_task_a(Task,[b(0,0,P)],[],R,Cost,_NewPos)),
true.
solve_task_a(_, _, _, _, 20, _).
solve_task_a(_, _, _, _, 30, _).
现在
?- solve_task(a, Cost).
Cost = 20.
?- solve_task(a, 30).
true.
?- solve_task(a, Cost), Cost = 30.
false.
通过彻底地测试 变量Cost
,将有一个简单的方法来解决这个问题,例如Cost >= 0
产生实例化错误应该 Cost
是一个未实例化的变量。但是如果你想(正如你在评论中指出的那样)确定成本,你需要这样说:
solve_task(Task,Cost):-
% agent_current_position(oscar,P),
once(solve_task_a(Task,[b(0,0,P)],[],R,CostX,_NewPos)),
CostX = Cost
true.
通过这种方式,我们可以确定 Cost
不会影响 solve_task_a/6
的结果(嗯 - 前提是 Cost
和 Task
之间没有别名 - 但是让我们暂时假设)。有人还说 输出统一放在提交 之后。
很多人会告诉您不需要如此额外的照顾,因为您永远不会以给定的成本使用 solve_task(Task, Cost)
。可能是这样,但你确定你会记住它吗?你确定源代码会记住它(没有任何动态检查)?如果您的心智能力负担过重,这种隐含的假设很容易积累到一定程度。
并不总是有一条简单的出路。但通常可以坚持逻辑纯粹性 logical-purity。在那种情况下,您不必记住任何此类假设。
无论如何,我建议您暂时不要进入 Prolog 的这些部分。而是坚持 successor-arithmetics、clpfd,以及其他保留 logical-purity 的干净、单调的程序。有很多东西要学!
在下面给出的代码中,有!
(剪切)修剪选择点以提高效率。我很确定 reverse
谓词和 agent_do_moves
谓词是必不可少的。
solve_task(Task,Cost):-
agent_current_position(oscar,P),
solve_task_a(Task,[b(0,0,P)],[],R,Cost,_NewPos),!, % prune choice point for efficiency
reverse(R,[_Init|Path]),
agent_do_moves(oscar,Path).
上面例子中的剪切效果如下:
理想情况下,它 提交 可能在 solve_task_a/6
内发生的搜索到找到的第一个答案。这释放了资源以寻找进一步的答案,从而改善 space 消费。
范围问题
然而,与此同时,它也可能隐藏 agent_current_position/2
的进一步答案。当然,对于这个目标有进一步的答案意义不大,但有可能是一个错误,恰好休眠了一段时间,才变得活跃,但在最坏的情况下仍然没有被发现。
出于这个原因,最好写而不是剪切
...,
once( solve_task_a( ... ) ),
...
这将范围精确地限制为您要表达的内容。
踏实度问题
但这不是唯一可能的问题来源。我看到这个变量 Cost
。调用solve_task(Task, Cost)
时会实例化还是不实例化?我可以在这里做很多猜测。但至少这个变量可能会影响 Prolog 将提交的答案。所以 solve_task(Task, 99)
和 solve_task(Task, Cost), Cost = 99
可能会产生不同的答案。事实上,后者甚至可能会失败。据说有这样问题的谓词缺乏坚定性.
为了说明在这种情况下如何容易失去坚定,请考虑您的(已经改进的)程序的这个(可运行的)草图:
solve_task(Task,Cost):- %agent_current_position(oscar,P),once(solve_task_a(Task,[b(0,0,P)],[],R,Cost,_NewPos)), true. solve_task_a(_, _, _, _, 20, _). solve_task_a(_, _, _, _, 30, _).
现在
?- solve_task(a, Cost).
Cost = 20.
?- solve_task(a, 30).
true.
?- solve_task(a, Cost), Cost = 30.
false.
通过彻底地测试 变量Cost
,将有一个简单的方法来解决这个问题,例如Cost >= 0
产生实例化错误应该 Cost
是一个未实例化的变量。但是如果你想(正如你在评论中指出的那样)确定成本,你需要这样说:
solve_task(Task,Cost):- %agent_current_position(oscar,P),once(solve_task_a(Task,[b(0,0,P)],[],R,CostX,_NewPos)), CostX = Cost true.
通过这种方式,我们可以确定 Cost
不会影响 solve_task_a/6
的结果(嗯 - 前提是 Cost
和 Task
之间没有别名 - 但是让我们暂时假设)。有人还说 输出统一放在提交 之后。
很多人会告诉您不需要如此额外的照顾,因为您永远不会以给定的成本使用 solve_task(Task, Cost)
。可能是这样,但你确定你会记住它吗?你确定源代码会记住它(没有任何动态检查)?如果您的心智能力负担过重,这种隐含的假设很容易积累到一定程度。
并不总是有一条简单的出路。但通常可以坚持逻辑纯粹性 logical-purity。在那种情况下,您不必记住任何此类假设。
无论如何,我建议您暂时不要进入 Prolog 的这些部分。而是坚持 successor-arithmetics、clpfd,以及其他保留 logical-purity 的干净、单调的程序。有很多东西要学!