6

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-''
         )
     )
    }
).

現在、多くのことを使用(;)して->、SWI-Prologがread_token(parser(Grammar, Tables), NewState, Token)Tail-Call-Optimizationを使用して再帰を最適化できるのか、または述語を手動でいくつかの句に分割する必要があるのか​​疑問に思いました。インタープリターが何をするのかを知る方法がわかりません。特に、デバッガーの実行時にTCOが無効になっていることを知っています。

4

1 に答える 1

6

あなたの質問に答えるために、私は最初に、最後の呼び出しの最適化を妨げる可能性のある「些細な」目標を探しました。見つかった場合:

     ; ( アース(入力)
         -> once(symbol:by_type_name(Tables, error, Index, _)),
             try_restore_input(入力、FailedInput、InputR)、
             入力 = [失敗した入力 | 入力R]、
             format(atom(Error), '~w', [FailedInput]),
             トークン = インデックス エラー
         ; once(symbol:by_type_name(Tables, eof, Index, _)),
             トークン = インデックス-''
         )

これらの 2 つのケースでは、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: 受け入れる (I, L),
            atom_concat(J、K、M)、
            P=lexer(dfa-H, last_accept-L, chars-M),
            R=N、
            read_token(parser(O, B),
                       P、
                       Q、
                       R、
                       S) % 1: いいね!
        ; ( T\=なし
            -> Q=TJ
            ; グラウンド(D)
            -> once(symbol:by_type_name(B, エラー, W, _)),
                try_restore_input(D、U、V)、
                D=[U|V],
                format(atom(X), '~w', [U]),
                Q=WX % 2: LCO を防止
            ; once(symbol:by_type_name(B, eof, W, _)),
                Q=W-'' % 3: LCO を防ぐ
            )、
            S=A     % 4: LCOを防止
        )。

ad 1) これはおそらくあなたが探している再帰的なケースです。ここでは、すべてが良さそうです。

広告 2,3) 上ですでに説明しましたが、目標を交換したいかもしれません

{}//1広告 4) これは、 DCG での処理方法が正確で確実な方法の効果です。経験則として: 実装者は、LCO らしさを追求するよりも、不動であることを好みます。参照してください: DCG 拡張: 安定性は無視されますか?

これには、呼び出しフレームの単純な再利用以外にも多くのことがあることに注意してください。ガベージ コレクションには多くの相互作用があります。SWI におけるこれらすべての問題を克服するには、追加の GC フェーズが必要でした。

詳細については、Prolog の Precise Garbage Collectionの小さなベンチマークを参照してください。

最後にあなたの質問に答えるために: あなたのルールは最適化されるかもしれません。ただし、再帰ゴールの前に選択ポイントが残っていない場合。


これに対する実際の低レベルのアプローチもあります。これをコード開発に使用することはありません: vm_list. このリストは、SWI が LCO を検討するかどうかを最終的に示しています (選択肢がない場合)。

i_calli_callmLCO を実行することはありません。のみ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 h_pop
   7 h_functor(レクサー/3)
   9 h_functor((-)/2)
  11 h_const(dfa)
  13 h_firstvar(7)
  15h_pop
  16 h_ファンクター((-)/2)
  18 h_const(last_accept)
  20 h_firstvar(8)
  22h_pop
  23 h_rfunctor((-)/2)
  25 h_const(文字)
  27 h_firstvar(9)
  29h_pop
  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:現在/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(テーブル,テーブル:item/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(レクサー/3)
 103 b_ファンクター((-)/2)
 105 b_const(dfa)
 107 b_argvar(15)
 109 ビーポップ
 110 b_ファンクター((-)/2)
 112 b_const(last_accept)
 114 b_argvar(18)
 116 ビーポップ
 117 b_rfunctor((-)/2)
 119 b_const(文字)
 121 b_argvar(19)
 123 ビーポップ
 124 b_unify_exit
 125 b_unify_fv(21,16)
 128 b_functor(パーサー/2)
 130 b_argvar(5)
 132 b_argvar(6)
 134 ビーポップ
 135 b_var(20)
 137b_var2
 138 b_var(21)
 140 b_var(4)
142 i_depart(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(なし)
 159 i_call((\=)/2)
 161 c_cut(27)
 163 b_unify_var(2)
 165 h_ファンクター((-)/2)
 167 h_var(8)
 169 h_var(9)
 171h_ポップ
 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 b_firstvar(10)
 188 i_call(グラウンド/1)
 190 c_cut(28)
 192 b_ファンクター((:)/2)
 194 b_const(シンボル)
 196 b_rfunctor(by_type_name/4)
 198 b_argvar(6)
 200 b_const(エラー)
 202 b_argfirstvar(22)
 204b_void
 205 ビーポップ
 206 i_call(1回/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 h_リスト
 219 h_var(23)
 221 h_var(24)
 223h_ポップ
 224b_unify_exit
 225 b_functor(アトム/1)
 227 b_argfirstvar(25)
 229 ビーポップ
 230 b_const('~w')
 232b_リスト
 233 b_argvar(23)
 235b_nil
 236 ビーポップ
 237 i_call(フォーマット/3)
 239 b_unify_var(2)
 241 h_ファンクター((-)/2)
 243 h_var(22)
 245h_var(25)
 247 h_ポップ
 248 b_unify_exit
 249 c_jmp(33)
 251b_ファンクター((:)/2)
 253 b_const(シンボル)
 255 b_rfunctor(by_type_name/4)
 257 b_argvar(6)
 259 b_const(eof)
 261 b_argfirstvar(22)
 263b_void
 264b_ポップ
 265 i_call(1回/1)
 267 b_unify_var(2)
 269 h_ファンクター((-)/2)
 271 h_var(22)
 273 h_const('')
 275h_ポップ
 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 i_exit
于 2013-03-15T16:07:42.013 に答える