这将在SWI-Prolog中进行优化吗?

问题描述

step_n(0,I,I).
step_n(N,In,Out) :-
    N > 0,plus(N1,1,N),phase_step(In,T),step_n(N1,T,Out).

phase_step是一种转换数据的功能。

step_n是否会在与phase_step相同的内存中运行?如果没有,我应该如何重写呢?这将取决于phase_step有一个解决方案吗?

编辑:使用prolog_current_frame进行调试后,我发现如果phase_step是一个像Out is In + 1这样的简单函数,则会进行优化,但不会在my use case中发生。

为什么TCO依赖于phase_step谓词?

解决方法

这是否取决于phase_step有一个解决方案?

种类很多,但仍然更强:这取决于Reassign Partitions是否具有确定性,这意味着不会留下任何“选择点”。选择点是未来的探索之路。不一定会产生进一步的解决方案,但Prolog仍然需要检查。

例如,这是确定性的:

Preferred Replica Election

它只有一个解决方案,并且Prolog不会提示我们提供更多信息:

phase_step

以下内容只有一个解决方案,但不是确定性的:

phase_step_det(X,X).

查看解决方案后,Prolog仍需要检查。即使我们通过查看代码可以知道某些东西(第二个子句)将失败:

?- phase_step_det(42,Out).
Out = 42.

以下解决方案不止一种,因此不是确定性的:

phase_step_extrafailure(X,X).
phase_step_extrafailure(_X,_Y) :-
    false.

为什么TCO依赖phase_step谓词?

如果要探索其他路径,那么必须在某处存储有关这些路径的数据。 “某处”是某种堆栈数据结构,并且对于每个将来的路径,堆栈上都必须有一个框架。这就是为什么您的内存使用量会增加。随之而来的是计算时间(以下使用您的?- phase_step_extrafailure(42,Out). Out = 42 ; false. 的副本以及上面相应的phase_step_twosolutions(X,X). phase_step_twosolutions(X,Y) :- plus(X,1,Y). ?- phase_step_twosolutions(42,Out). Out = 42 ; Out = 43. 变体):

step_n

探索此问题的一种方法是使用SWI-Prolog调试器,该调试器可以向您显示替代方法(=选择点=要探索的未来路径):

phase_step

所有这些替代方法都对应于额外的解释器框架。如果您使用SWI-Prolog的可视调试器,它还将向您显示堆栈的图形表示,包括所有开放选择点(尽管我总是觉得很难理解)。

因此,如果您想要TCO而不增加堆栈,则需要执行阶段步骤以确定性地执行。您可以通过使?- time(step_n_det(100_000,42,Out)). % 400,002 inferences,0.017 CPU in 0.017 seconds (100% CPU,24008702 Lips) Out = 42 ; % 7 inferences,0.000 CPU in 0.000 seconds (87% CPU,260059 Lips) false. ?- time(step_n_extrafailure(100_000,000 inferences,4.288 CPU in 4.288 seconds (100% CPU,93282 Lips) Out = 42 ; % 100,005 inferences,0.007 CPU in 0.007 seconds (100% CPU,13932371 Lips) false. ?- time(step_n_twosolutions(100_000,4.231 CPU in 4.231 seconds (100% CPU,94546 Lips) Out = 42 ; % 4 inferences,548 Lips) Out = 43 ; % 8 inferences,0.005 CPU in 0.005 seconds (100% CPU,1612 Lips) Out = 43 ; % 4 inferences,0.008 CPU in 0.008 seconds (100% CPU,489 Lips) Out = 44 ; % 12 inferences,0.003 CPU in 0.003 seconds (100% CPU,4396 Lips) Out = 43 ; % 4 inferences,0.009 CPU in 0.009 seconds (100% CPU,451 Lips) Out = 44 . % many further solutions 谓词本身具有确定性来做到这一点。您还可以在?- trace,step_n_det(5,Out). Call: (9) step_n_det(5,_1496) ? skip % I typed 's' here. Exit: (9) step_n_det(5,42) ? alternatives % I typed 'A' here. [14] step_n_det(0,42) Exit: (9) step_n_det(5,42) ? no debug % I typed 'n' here. Out = 42 ; false. ?- trace,step_n_extrafailure(5,Out). Call: (9) step_n_extrafailure(5,_1500) ? skip Exit: (9) step_n_extrafailure(5,42) ? alternatives [14] step_n_extrafailure(0,42) [14] phase_step_extrafailure(42,42) [13] phase_step_extrafailure(42,42) [12] phase_step_extrafailure(42,42) [11] phase_step_extrafailure(42,42) [10] phase_step_extrafailure(42,42) Exit: (9) step_n_extrafailure(5,42) ? no debug Out = 42 ; false. 内的phase_step通话之后进行剪切。

以下是每个phase_step之后从上方打来的电话:

step_n

仅当您了解真正需要它们的位置和原因后,才可以盲目放置切口。请注意,在phase_step情况下,切口仅能消除故障,而在?- time(step_n_det(100_000,001 inferences,24204529 Lips) Out = 42 ; % 7 inferences,0.000 CPU in 0.000 seconds (83% CPU,737075 Lips) false. ?- time(step_n_extrafailure(100_000,0.023 CPU in 0.023 seconds (100% CPU,17573422 Lips) Out = 42 ; % 5 inferences,0.000 CPU in 0.000 seconds (93% CPU,220760 Lips) false. ?- time(step_n_twosolutions(100_000,17732727 Lips) Out = 42 ; % 5 inferences,0.000 CPU in 0.000 seconds (94% CPU,219742 Lips) false. 情况下,切口能去除实际的解决方案。

,

一个用于了解代码性能问题(尤其是不需要的不确定性)的有用工具是 ports profiler 工具,例如ECLiPSe和Logtalk。 Logtalk ports_profiler工具是可移植的,因此我们可以在这里使用它。我们首先包装您的代码(从您的要点链接开始):

:- use_module(library(lists),[]).


:- object(step).

    :- public(step_n/3).
    :- use_module(lists,[reverse/2]).

    % pattern for the nth digit mth-coeffcient
    digit_m(N,M,D) :-
        divmod(M,N,Q,_),divmod(Q,4,_,C),(C = 0,D = 0; C = 1,D = 1; C = 2,D = 0; C = 3,D = -1).
    
    calculate_digit_n(N,In,D) :-
        calculate_digit_n_(N,D,0).
    calculate_digit_n_(_,[],Acc) :- D1 is abs(Acc),divmod(D1,10,D).
    calculate_digit_n_(N,[I | Is],Acc) :-
        digit_m(N,P is C*I,M1 is M+1,Acc1 is Acc+P,calculate_digit_n_(N,Is,M1,Acc1).
    
    phase_step(In,Out) :-
        length(In,L),L1 is L + 1,phase_step_(In,Out,L1,[]).
    phase_step_(_,L,Acc) :- reverse(Out,Acc).
    phase_step_(In,Acc) :-
        N < L,calculate_digit_n(N,D),N1 is N + 1,N1,[D | Acc]).
    
    step_n(0,I,I).
    step_n(N,Out) :-
        prolog_current_frame(Fr),format('~w ',Fr),N > 0,N1 is N - 1,phase_step(In,T),step_n(N1,T,Out).

:- end_object.

%:- step_n(10,[1,2,3,5,6,7,8],X).

然后(使用SWI-Prolog作为后端,因为这是您告诉我们您正在使用的Prolog系统):

$ swilgt
...
?- {ports_profiler(loader)}.
% [ /Users/pmoura/logtalk/tools/ports_profiler/ports_profiler.lgt loaded ]
% [ /Users/pmoura/logtalk/tools/ports_profiler/loader.lgt loaded ]
% (0 warnings)
true.

?- logtalk_load(step,[debug(on),source_data(on)]).
% [ /Users/pmoura/step.pl loaded ]
% (0 warnings)
true.

?- step::step_n(10,X).
340 15578 30816 46054 61292 76530 91768 107006 122244 137482 
X = [3,8] .

?- ports_profiler::data.
------------------------------------------------------------------------------
Entity  Predicate               Fact  Rule  Call  Exit *Exit  Fail  Redo Error
------------------------------------------------------------------------------
step    calculate_digit_n/3        0    80    80     0    80     0     0     0
step    calculate_digit_n_/5       0   720   720     0   720     0     0     0
step    digit_m/3                  0   640   640    40   600     0     0     0
step    phase_step/2               0    10    10     0    10     0     0     0
step    phase_step_/5              0    90    90     0    90     0     0     0
step    step_n/3                   1    10    11     0    11     0     0     0
------------------------------------------------------------------------------
true.

*Exit列用于确定过程框中是否存在不确定性。要获得有关该工具和解释表结果的帮助,请参见https://logtalk.org/manuals/devtools/ports_profiler.html,但只要一眼就可以看出表phase_step/2step_n/3都是不确定的。

更新

请注意,尾部调用优化(TCO)并不意味着或不需要谓词具有确定性。在您的情况下,Prolog编译器可以应用TCO ,因为step_n/3谓词的规则中的 last 调用是对自身的调用。这意味着可以在 特定的递归调用中保存堆栈框架。这并不意味着递归调用之前没有创建选择点。使用once/1(如您在评论中所提到的)仅会丢弃调用phase_step/2时创建的选择点,因为谓词本身是不确定的。这就是表格显示的内容。 step_n/3谓词也是非确定性的,因此在第一个参数为0时调用它会创建一个选择点,当您在第一个参数上调用谓词为零时会发生选择点(当查询的证明达到此递归定义的基本情况时)。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...