如何确定Prolog是否执行尾部呼叫优化

Seb*_*ian 5 tail-recursion prolog tail-call-optimization dcg

使用SWI Prolog(Win x64)的开发版本,我为确定性词法分析器(托管在github上)写了DCG谓词(因此,所有外部谓词都没有选择点):

read_token(parser(Grammar, Tables),
       lexer(dfa-DFAIndex, last_accept-LastAccept, chars-Chars0),
       Token) -->
(   [Input],
    {
     dfa:current(Tables, DFAIndex, DFA),
     char_and_code(Input, Char, Code),
     dfa:find_edge(Tables, DFA, Code, TargetIndex)
    }
->  { table:item(dfa_table, Tables, TargetIndex, TargetDFA),
      dfa:accept(TargetDFA, Accept),
      atom_concat(Chars0, Char, Chars),
      NewState = lexer(dfa-TargetIndex,
                       last_accept-Accept,
                       chars-Chars)
    },
    read_token(parser(Grammar, Tables), NewState, Token)
;   {
     (   LastAccept \= none
     ->  Token = LastAccept-Chars0
     ;   (   ground(Input)
         ->  once(symbol:by_type_name(Tables, error, Index, _)),
             try_restore_input(Input, FailedInput, InputR),
             Input = [FailedInput | InputR],
             format(atom(Error), '~w', [FailedInput]),
             Token = Index-Error
         ;   once(symbol:by_type_name(Tables, eof, Index, _)),
             Token = Index-''
         )
     )
    }
).
Run Code Online (Sandbox Code Playgroud)

现在,使用(;)->了很多,我想知道SWI-Prolog的可优化递归read_token(parser(Grammar, Tables), NewState, Token)使用尾部调用优化,或者如果我有谓词手动分割成几个条款。我只是不知道如何找出解释器的作用,特别是知道在运行调试器时禁用了TCO。

fal*_*lse 5

为了回答您的问题,我首先寻找了可能阻碍上次通话优化的“琐碎”目标。如果发现一些:

     ; (地面(输入)
         ->一次(符号:by_type_name(表,错误,索引,_)),
             try_restore_input(Input,FailedInput,InputR),
             输入= [失败输入| InputR],
             格式(atom(Error),'〜w',[FailedInput]),
             令牌=索引错误
         ; 一次(符号:by_type_name(表,eof,索引,_)),
             代币=索引-''

在这两种情况下,仅通过这些目标就可以防止LCO。

现在,我遵守了您的规则,并通过以下方式查看了扩展listing

?-列表(read_token)。
read_token(parser(O,B),lexer(dfa-C,last_accept-T,chars-J),Q,A,S):-
        (A = [D | G],
            dfa:current(B,C,E),
            char_and_code(D,K,F),
            dfa:find_edge(B,E,F,H),
            N = G
        -> table:item(dfa_table,B,H,I),
            dfa:accept(I,L),
            atom_concat(J,K,M),
            P = lexer(dfa-H,last_accept-L,chars-M),
            R = N,
            read_token(parser(O,B),
                       P,
                       问
                       R,
                       S)%1:看起来不错!
        ; (T \ =无
            -> Q = TJ
            ; 地面(D)
            ->一次(符号:by_type_name(B,错误,W,_)),
                try_restore_input(D,U,V),
                D = [U | V],
                格式(atom(X),'〜w',[U]),
                Q = WX%2:防止LCO
            ; 一次(符号:by_type_name(B,eof,W,_)),
                Q = W-''%3:防止LCO
            ),
            S = A     %4:防止LCO
        )。

广告1)这是您很可能在寻找的递归案例。在这里,一切似乎都很好。

广告2,3)上面已经讨论过,也许您想交换目标

广告4)这是{}//1DCG中处理方式精确,坚定的结果。根据经验:与实施LCO风格相比,实施者宁愿坚定不移。请参阅:DCG扩展:牢固性是否被忽略?

还请注意,除了简单重复使用调用帧外,还有很多其他功能。垃圾回收有很多交互作用。为了克服SWI中的所有这些问题,有必要增加一个GC阶段。

有关更多信息,请参考Prolog中的Precise Garbage Collection中的微小基准。

因此,最后回答您的问题:您的规则可能已优化;前提是递归目标之前没有选择点。


也有真正的底层方法。我从未将其用于代码开发:vm_list。该清单最终显示了SWI是否可以考虑LCO(假设那里没有选择点)。

i_call并且i_callm永远不会执行LCO。只有i_depart会做。在:142 i_depart(read_token/5)

?-vm_list(read_token)。
================================================== ======================
read_token / 5
================================================== ======================
   0 s_virgin
   1次i_exit
----------------------------------------
条款1((0x1cc4710)):
----------------------------------------
   0 h_functor(解析器/ 2)
   2 h_firstvar(5)
   4 h_firstvar(6)
   6小时
   7 h_functor(lexer / 3)
   9 h_functor((-)/ 2)
  11 h_const(dfa)
  13 h_firstvar(7)
  15小时
  16 h_functor((-)/ 2)
  18 h_const(last_accept)
  20小时_firstvar(8)
  22小时
  23 h_rfunctor((-)/ 2)
  25 h_const(字符)
  27 h_firstvar(9)
  29小时
  30 i_enter
  31 c_ifthenelse(26,118)
  34 b_unify_var(3)
  36 h_list_ff(10,11)
  39 b_unify_exit
  40 b_var(6)
  42 b_var(7)
  44 b_firstvar(12)
  46 i_callm(dfa,dfa:current / 3)
  49 b_var(10)
  51 b_firstvar(13)
  53 b_firstvar(14)
  55 i_call(char_and_code / 3)
  57 b_var(6)
  59 b_var(12)
  61 b_var(14)
  63 b_firstvar(15)
  65 i_callm(dfa,dfa:find_edge / 4)
  68 b_unify_fv(16,11)
  71 c_cut(26)
  73 b_const(dfa_table)
  75 b_var(6)
  77 b_var(15)
  79 b_firstvar(17)
  81 i_callm(表格,表格:项目/ 4)
  84 b_var(17)
  86 b_firstvar(18)
  88 i_callm(dfa,dfa:accept / 2)
  91 b_var(9)
  93 b_var(13)
  95 b_firstvar(19)
  97 i_call(atom_concat / 3)
  99 b_unify_firstvar(20)
 101 b_functor(lexer / 3)
 103 b_functor((-)/ 2)
 105 b_const(dfa)
 107 b_argvar(15)
 109 b_pop
 110 b_functor((-)/ 2)
 112 b_const(last_accept)
 114 b_argvar(18)
 116 b_pop
 117 b_rfunctor((-)/ 2)
 119 b_const(字符)
 121 b_argvar(19)
 123 b_pop
 124 b_unify_exit
 125 b_unify_fv(21,16)
 128 b_functor(解析器/ 2)
 130 b_argvar(5)
 第132章(一更)
 134 b_pop
 135 b_var(20)
 第137章
 138 b_var(21)
 140 b_var(4)
 142我_离开(read_token / 5)
 144 c_var_n(22,2)
 147 c_var_n(24,2)
 150 c_jmp(152)
 152 c_ifthenelse(27,28)
 155 b_var(8)
 157 b_const(none)
 第159章(小幸运)
 161 c_cut(27)
 163 b_unify_var(2)
 165 h_functor((-)/ 2)
 167小时_var(8)
 169 h_var(9)
 171小时
 172 b_unify_exit
 173 c_var(10)
 175 c_var_n(22,2)
 178 c_var_n(24,2)
 181 c_jmp(101)
 183 c_ifthenelse(28,65)
 第186章(二更)
 188我叫(地面/ 1)
 190 c_cut(28)
 第192章(二更)
 194 b_const(符号)
 第196章(一更)
 第198章(二更)
 200 b_const(错误)
 202 b_argfirstvar(22)
 204 b_void
 205 b_pop
 206 i_call(一次/ 1)
 208 b_var(10)
 210 b_firstvar(23)
 212 b_firstvar(24)
 214 i_call(try_restore_input / 3)
 216 b_unify_var(10)
 第218章
 219 h_var(23)
 221 h_var(24)
 223小时
 224 b_unify_exit
 225 b_functor(atom / 1)
 第227章(二更)
 229 b_pop
 230 b_const('〜w')
 232 b_list
 233 b_argvar(23)
 235 b_nil
 236 b_pop
 237 i_call(format / 3)
 第239章(二更)
 241 h_functor((-)/ 2)
 243小时_var(22)
 245小时_var(25)
 247小时
 248b_unify_exit
 249 c_jmp(33)
 251 b_functor((:)/ 2)
 253 b_const(symbol)
 255 b_rfunctor(by_type_name / 4)
 第257章(二更)
 第259章
 261 b_argfirstvar(22)
 第263章
 264 b_pop
 265 i_call(一次/ 1)
 第267章(二更)
 269 h_functor((-)/ 2)
 271 h_var(22)
 第273章
 275小时
 276 b_unify_exit
 277 c_var(10)
 279 c_var_n(23,2)
 282 c_var(25)
 284 b_unify_vv(4,3)
 287 c_var_n(11,2)
 290 c_var_n(13,2)
 293 c_var_n(15,2)
 296 c_var_n(17,2)
 299 c_var_n(19,2)
 302 c_var(21)
 第304章