如何使用带有CLP(FD)和多个约束的DCG枚举组合

时间:2017-03-08 13:22:36

标签: prolog dcg clpfd implicit-state-passing

此问题从Mat的answerAlgorithm improvement for enumerating binary trees开始,它只有一个输入值,用于确定二叉树的所有节点数,并且需要能够有两个输入值,其中一个是一元节点的数量,另一个是二进制节点的数量。

虽然我能够使用listing/1并线程化额外的状态变量来推导出一个解决方案:

e(t, B, B, U, U).

e(u(E), B0, B1, [_|U0], U1) :-
    e(E, B0, B1, U0, U1).

e(b(E0, E1), [_|B0], B2, U0, U2) :-
    e(E0, B0, B1, U0, U1),
    e(E1, B1, B2, U1, U2).

e(U,B,Es) :-
    length(Bs, B),
    length(Us, U),
    e(Es,Bs,[],Us,[]).

注意:请参阅下面的Prolog输出。

我对length/2作为约束的使用感到不满意,因为它在使用中并不明显,并且它没有使用DCG。从以前对其他问题的其他尝试我知道使用数字作为约束会失败,例如

e_a(t, B, B, U, U).

e_a(u(E), B0, B1, U0, U2) :-
    U1 is U0 + 1,
    e_a(E, B0, B1, U1, U2).

e_a(b(E0, E1), B0, B3, U0, U2) :-
    B1 is B0 + 1,
    e_a(E0, B1, B2, U0, U1),
    e_a(E1, B2, B3, U1, U2).

e_a(U,B,Es) :-
    U =:= Us,   % Arguments are not sufficiently instantiated 1=:=_2692
    B =:= Bs,
    e_a(Es,0,Bs,0,Us).

?- e_a(1,2,Es).

然而,在搜索时,我发现了带有DCG的use CLP(FD),并决定试试。

:-use_module(library(clpfd)).

e_b(t, B, B, U, U).

e_b(u(E), B0, B1, U0, U2) :-
    U1 #= U0 + 1,
    e_b(E, B0, B1, U1, U2).

e_b(b(E0, E1), B0, B3, U0, U2) :-
    B1 #= B0 + 1,
    e_b(E0, B1, B2, U0, U1),
    e_b(E1, B2, B3, U1, U2).

e_b(U,B,Es) :-
    U #=< Us,
    B #=< Bs,
    e_b(Es,0,Bs,0,Us).

?- e_b(1,2,Es).

然而,这导致无限循环返回无结果 注意:我理解CLP(FD)的概念,但我对它的实际应用几乎没有。

所以问题是:

  1. CLP(FD)可以与此解决方案一起使用吗?如果可以,如何使用?
  2. 如何将非DCG解决方案转换回DCG版本?
  3. 补编

    开始代码和列表

    e(number)        --> [].
    e(u(Arg))        --> [_], e(Arg).
    e(b(Left,Right)) --> [_,_], e(Left), e(Right).
    
    ?- listing(e).
    e(t, A, A).
    e(u(A), [_|B], C) :-
            e(A, B, C).
    e(b(A, C), [_, _|B], E) :-
            e(A, B, D),
            e(C, D, E).
    

    Prolog输出

    ?- e(1,2,Es).
    Es = u(b(t, b(t, t))) ;
    Es = u(b(b(t, t), t)) ;
    Es = b(t, u(b(t, t))) ;
    Es = b(t, b(t, u(t))) ;
    Es = b(t, b(u(t), t)) ;
    Es = b(u(t), b(t, t)) ;
    Es = b(u(b(t, t)), t) ;
    Es = b(b(t, t), u(t)) ;
    Es = b(b(t, u(t)), t) ;
    Es = b(b(u(t), t), t) ;
    false.
    

    答案清单

    对于那些不熟悉DCG的人,在Prolog工具箱中有一个导入工具是listing/1,它会将DCG转换为标准Prolog。

    e.g。

    ?- listing(expression).  
    

    对于以下列表,我还手动更改了变量的名称,以便更容易理解和理解。当DCG转换为标准Prolog时,两个额外变量可能会显示为谓词的最后两个参数。我在这里改了名字。它们将以S0作为倒数第二个参数开始,然后以S1S2进行,依此类推,直到它们成为最后一个参数。此外,如果其中一个输入参数穿过代码,我更改了名称,例如UU0等等。我还添加了clp(fd)约束作为注释。

    在答案的一部分上使用listing/1

    % DCG
    expression(U, B, E) -->
          terminal(U, B, E)
        | unary(U, B, E)
        | binary(U, B, E).
    
    
    % Standard Prolog
    expression(U, B, E, S0, S1) :-
            (   terminal(U, B, E, S0, S1)
            ;   unary(U, B, E, S0, S1)
            ;   binary(U, B, E, S0, S1)
            ).
    


    % DCG
    terminal(0, 0, t) --> [t].
    
    % Standard Prolog
    terminal(0, 0, t, [t|S0], S0).
    


    % DCG
    unary(U, B, u(E)) -->
        {
            U1 #>= 0,
            U #= U1 + 1
        },
        ['u('],
        expression_1(U1, B, E),
        [')'].
    
    % Standard Prolog
    unary(U0, B, u(E), S0, S4) :-
            true,
            clpfd:clpfd_geq(U1, 0),               % U1 #>= 0
            (   integer(U0)
            ->  (   integer(U1)
                ->  U0=:=U1+1                     % U #= U1 + 1
                ;   U2=U0,
                    clpfd:clpfd_equal(U2, U1+1)   % U #= U1 + 1
                )
            ;   integer(U1)
            ->  (   var(U0)
                ->  U0 is U1+1                    % U #= U1 + 1
                ;   U2 is U1+1,                   % U #= U1 + 1
                    clpfd:clpfd_equal(U0, U2)
                )
            ;   clpfd:clpfd_equal(U0, U1+1)       % U #= U1 + 1
            ),
            S1=S0,
            S1=['u('|S2],
            expression_1(U1, B, E, S2, S3),
            S3=[')'|S4].
    


    % DCG
    binary(U, B, b(E1, E2)) -->
        {
            U1 #>= 0,
            U2 #>= 0,
            U #= U1 + U2,
            B1 #>= 0,
            B2 #>= 0,
            B #= B1 + B2 + 1
        },
        ['b('],
        expression_1(U1, B1, E1),
        expression_1(U2, B2, E2),
        [')'].
    
    % Standard Prolog
    binary(U0, B0, b(E1, E2), S0, S5) :-
            true,
            clpfd:clpfd_geq(U1, 0),                 % U1 #>= 0
            true,
            clpfd:clpfd_geq(U2, 0),                 % U2 #>= 0
            (   integer(U0)
            ->  (   integer(U1),
                    integer(U2)
                ->  U0=:=U1+U2                      % U #= U1 + 1
                ;   U3=U0,
                    clpfd:clpfd_equal(U3, U1+U2)    % U #= U1 + 1
                )
            ;   integer(U1),
                integer(U2)
            ->  (   var(U0)
                ->  U0 is U1+U2                     % U #= U1 + 1
                ;   U3 is U1+U2,                    % U #= U1 + 1
                    clpfd:clpfd_equal(U0, U3)
                )
            ;   clpfd:clpfd_equal(U0, U1+U2)        % U #= U1 + 1
            ),
            true,
            clpfd:clpfd_geq(B1, 0),                 % B1 #>= 0
            true,
            clpfd:clpfd_geq(B2, 0),                 % B2 #>= 0
            (   integer(B0)
            ->  (   integer(B1),
                    integer(B2)
                ->  B0=:=B1+B2+1                    % B #= B1 + B2 + 1
                ;   B3=B0,
                    clpfd:clpfd_equal(B3, B1+B2+1)  % B #= B1 + B2 + 1
                )
            ;   integer(B1),
                integer(B2)
            ->  (   var(B0)
                ->  B0 is B1+B2+1                   % B #= B1 + B2 + 1
                ;   B3 is B1+B2+1,                  % B #= B1 + B2 + 1
                    clpfd:clpfd_equal(B0, B3)
                )
            ;   clpfd:clpfd_equal(B0, B1+B2+1)      % B #= B1 + B2 + 1
            ),
            S1=S0,
            S1=['b('|S2],
            expression_1(U1, B1, E1, S2, S3),
            expression_1(U2, B2, E2, S3, S4),
            S4=[')'|S5].
    

    参考SWI-Prolog源代码

    如果你想看到将clp(fd)或DCG翻译成标准prolog的来源,请点击链接。

    备注

    将这些视为我的个人笔记,以防我将来再回到这个问题。如果他们可以帮助他人,就没有意义保持自己。

    关于

      

    何时使用长度/ 2来约束DCG结果的大小以及何时可以使用CLP(FD)?

    在查看使用clp(fd)作为约束的代码列表之后,我可以开始理解为什么使用构建并行列表并使用length/2。我没想到代码会那么复杂。

    关于clp(fd)如何避免导致错误

      

    参数未充分实例化1 =:= _ 2692

    可以看出它检查变量是否绑定

    e.g。

    integer(U1)
    var(U0)
    

    代码的演变

    基于@lurker的答案,我能够将代码演化为此,这将能够生成唯一的一元二元树的所有组合,给出一元操作列表,二元操作列表和列表终端。虽然它可以生成表达式的组合,但在用于生成我需要的表达式之前,仍然需要一个中间步骤来重新排列三个列表中项目的顺序。

    % order of predicates matters
    e(    Uc     , Uc , Bc     , Bc , [Terminal|Terminal_s], Terminal_s  , Unary_op_s             , Unary_op_s  , Binary_op_s              , Binary_op_s  , t         , Terminal             ).
    
    e(    [_|Uc0], Uc1, Bc0    , Bc1, Terminal_s_0         , Terminal_s_1, [Unary_op|Unary_op_s_0], Unary_op_s_1, Binary_op_s_0            , Binary_op_s_1, u(E0)     , [op(Unary_op),[UE]]  ) :-
        e(Uc0    , Uc1, Bc0    , Bc1, Terminal_s_0         , Terminal_s_1, Unary_op_s_0           , Unary_op_s_1, Binary_op_s_0            , Binary_op_s_1, E0        , UE                   ).
    
    e(    Uc0    , Uc2, [_|Bc0], Bc2, Terminal_s_0         , Terminal_s_2, Unary_op_s_0           , Unary_op_s_2, [Binary_op|Binary_op_s_0], Binary_op_s_2, b(E0, E1), [op(Binary_op),[L,R]] ) :-
        e(Uc0    , Uc1, Bc0    , Bc1, Terminal_s_0         , Terminal_s_1, Unary_op_s_0           , Unary_op_s_1, Binary_op_s_0            , Binary_op_s_1, E0       , L                     ),
        e(Uc1    , Uc2, Bc1    , Bc2, Terminal_s_1         , Terminal_s_2, Unary_op_s_1           , Unary_op_s_2, Binary_op_s_1            , Binary_op_s_2, E1       , R                     ).
    
    e(Uc, Bc, Terminal_s, Unary_op_s, Binary_op_s, Es, Ls) :-
        length(Bs, Bc),
        length(Us, Uc),
        e(Us,[], Bs,[], Terminal_s, _, Unary_op_s, _, Binary_op_s, _, Es, Ls).
    
    e(Unary_op_s, Binary_op_s, Terminal_s, Es, Ls) :-
        length(Unary_op_s,Uc),
        length(Binary_op_s,Bc),
        length(Terminal_s,Ts),
        Tc is Bc + 1,
        Ts == Tc,
        e(Uc, Bc, Terminal_s, Unary_op_s, Binary_op_s, Es, Ls).
    

    这是我需要的部分

    ?- e([neg,ln],[add,sub],[[number(0)],[number(1)],[number(2)]],_,Ls);true.
    Ls = [op(neg), [[op(ln), [[op(add), [[number(0)], [op(sub), [[number(1)], [number(2)]]]]]]]]] ;
    Ls = [op(neg), [[op(ln), [[op(add), [[op(sub), [[number(0)], [number(1)]]], [number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(ln), [[op(sub), [[number(1)], [number(2)]]]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(sub), [[number(1)], [op(ln), [[number(2)]]]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(sub), [[op(ln), [[number(1)]]], [number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[op(ln), [[number(0)]]], [op(sub), [[number(1)], [number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[op(ln), [[op(sub), [[number(0)], [number(1)]]]]], [number(2)]]]]] ;
    Ls = [op(neg), [[op(add), [[op(sub), [[number(0)], [number(1)]]], [op(ln), [[number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[op(sub), [[number(0)], [op(ln), [[number(1)]]]]], [number(2)]]]]] ;
    Ls = [op(neg), [[op(add), [[op(sub), [[op(ln), [[number(0)]]], [number(1)]]], [number(2)]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(ln), [[op(sub), [[number(1)], [number(2)]]]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(sub), [[number(1)], [op(ln), [[number(2)]]]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(sub), [[op(ln), [[number(1)]]], [number(2)]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[number(1)], [op(neg), [[op(ln), [[number(2)]]]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[op(neg), [[number(1)]]], [op(ln), [[number(2)]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[op(neg), [[op(ln), [[number(1)]]]]], [number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(ln), [[op(sub), [[number(1)], [number(2)]]]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(sub), [[number(1)], [op(ln), [[number(2)]]]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(sub), [[op(ln), [[number(1)]]], [number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[op(ln), [[number(0)]]]]], [op(sub), [[number(1)], [number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[op(ln), [[op(sub), [[number(0)], [number(1)]]]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(neg), [[op(sub), [[number(0)], [number(1)]]]]], [op(ln), [[number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[op(sub), [[number(0)], [op(ln), [[number(1)]]]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(neg), [[op(sub), [[op(ln), [[number(0)]]], [number(1)]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [number(1)]]], [op(neg), [[op(ln), [[number(2)]]]]]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [op(neg), [[number(1)]]]]], [op(ln), [[number(2)]]]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [op(neg), [[op(ln), [[number(1)]]]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[op(neg), [[number(0)]]], [number(1)]]], [op(ln), [[number(2)]]]]] ;
    Ls = [op(add), [[op(sub), [[op(neg), [[number(0)]]], [op(ln), [[number(1)]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[op(neg), [[op(ln), [[number(0)]]]]], [number(1)]]], [number(2)]]] ;
    true.
    

    这是一个很好的快速方式,可以看出它们是独一无二的。

    ?- e([neg,ln],[add,sub],[[number(0)],[number(1)],[number(2)]],Es,_);true.
    Es = u(u(b(t, b(t, t)))) ;
    Es = u(u(b(b(t, t), t))) ;
    Es = u(b(t, u(b(t, t)))) ;
    Es = u(b(t, b(t, u(t)))) ;
    Es = u(b(t, b(u(t), t))) ;
    Es = u(b(u(t), b(t, t))) ;
    Es = u(b(u(b(t, t)), t)) ;
    Es = u(b(b(t, t), u(t))) ;
    Es = u(b(b(t, u(t)), t)) ;
    Es = u(b(b(u(t), t), t)) ;
    Es = b(t, u(u(b(t, t)))) ;
    Es = b(t, u(b(t, u(t)))) ;
    Es = b(t, u(b(u(t), t))) ;
    Es = b(t, b(t, u(u(t)))) ;
    Es = b(t, b(u(t), u(t))) ;
    Es = b(t, b(u(u(t)), t)) ;
    Es = b(u(t), u(b(t, t))) ;
    Es = b(u(t), b(t, u(t))) ;
    Es = b(u(t), b(u(t), t)) ;
    Es = b(u(u(t)), b(t, t)) ;
    Es = b(u(u(b(t, t))), t) ;
    Es = b(u(b(t, t)), u(t)) ;
    Es = b(u(b(t, u(t))), t) ;
    Es = b(u(b(u(t), t)), t) ;
    Es = b(b(t, t), u(u(t))) ;
    Es = b(b(t, u(t)), u(t)) ;
    Es = b(b(t, u(u(t))), t) ;
    Es = b(b(u(t), t), u(t)) ;
    Es = b(b(u(t), u(t)), t) ;
    Es = b(b(u(u(t)), t), t) ;
    true.
    

    如果您一直在阅读评论,那么您就知道可以使用一个列表作为约束或没有列表作为约束。

    如果使用

    禁用列表作为约束
    e(Uc, Bc, Terminal_s, Unary_op_s, Binary_op_s, Es, Ls) :-
        e(_,[], _,[], Terminal_s, _, Unary_op_s, _, Binary_op_s, _, Es, Ls).
    

    你得到了

    ?- e([neg,ln],[add,sub],[[number(0)],[number(1)],[number(2)]],_,Ls);true.
    Ls = [number(0)] ;
    Ls = [op(neg), [[number(0)]]] ;
    Ls = [op(neg), [[op(ln), [[number(0)]]]]] ;
    Ls = [op(neg), [[op(ln), [[op(add), [[number(0)], [number(1)]]]]]]] ;
    Ls = [op(neg), [[op(ln), [[op(add), [[number(0)], [op(sub), [[number(1)], [number(2)]]]]]]]]] ;
    Ls = [op(neg), [[op(ln), [[op(add), [[op(sub), [[number(0)], [number(1)]]], [number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [number(1)]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(ln), [[number(1)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(ln), [[op(sub), [[number(1)], [number(2)]]]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(sub), [[number(1)], [number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(sub), [[number(1)], [op(ln), [[number(2)]]]]]]]]] ;
    Ls = [op(neg), [[op(add), [[number(0)], [op(sub), [[op(ln), [[number(1)]]], [number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[op(ln), [[number(0)]]], [number(1)]]]]] ;
    Ls = [op(neg), [[op(add), [[op(ln), [[number(0)]]], [op(sub), [[number(1)], [number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[op(ln), [[op(sub), [[number(0)], [number(1)]]]]], [number(2)]]]]] ;
    Ls = [op(neg), [[op(add), [[op(sub), [[number(0)], [number(1)]]], [number(2)]]]]] ;
    Ls = [op(neg), [[op(add), [[op(sub), [[number(0)], [number(1)]]], [op(ln), [[number(2)]]]]]]] ;
    Ls = [op(neg), [[op(add), [[op(sub), [[number(0)], [op(ln), [[number(1)]]]]], [number(2)]]]]] ;
    Ls = [op(neg), [[op(add), [[op(sub), [[op(ln), [[number(0)]]], [number(1)]]], [number(2)]]]]] ;
    Ls = [op(add), [[number(0)], [number(1)]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[number(1)]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(ln), [[number(1)]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(ln), [[op(sub), [[number(1)], [number(2)]]]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(sub), [[number(1)], [number(2)]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(sub), [[number(1)], [op(ln), [[number(2)]]]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(neg), [[op(sub), [[op(ln), [[number(1)]]], [number(2)]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[number(1)], [number(2)]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[number(1)], [op(neg), [[number(2)]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[number(1)], [op(neg), [[op(ln), [[number(2)]]]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[op(neg), [[number(1)]]], [number(2)]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[op(neg), [[number(1)]]], [op(ln), [[number(2)]]]]]]] ;
    Ls = [op(add), [[number(0)], [op(sub), [[op(neg), [[op(ln), [[number(1)]]]]], [number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [number(1)]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(ln), [[number(1)]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(ln), [[op(sub), [[number(1)], [number(2)]]]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(sub), [[number(1)], [number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(sub), [[number(1)], [op(ln), [[number(2)]]]]]]] ;
    Ls = [op(add), [[op(neg), [[number(0)]]], [op(sub), [[op(ln), [[number(1)]]], [number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[op(ln), [[number(0)]]]]], [number(1)]]] ;
    Ls = [op(add), [[op(neg), [[op(ln), [[number(0)]]]]], [op(sub), [[number(1)], [number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[op(ln), [[op(sub), [[number(0)], [number(1)]]]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(neg), [[op(sub), [[number(0)], [number(1)]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(neg), [[op(sub), [[number(0)], [number(1)]]]]], [op(ln), [[number(2)]]]]] ;
    Ls = [op(add), [[op(neg), [[op(sub), [[number(0)], [op(ln), [[number(1)]]]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(neg), [[op(sub), [[op(ln), [[number(0)]]], [number(1)]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [number(1)]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [number(1)]]], [op(neg), [[number(2)]]]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [number(1)]]], [op(neg), [[op(ln), [[number(2)]]]]]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [op(neg), [[number(1)]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [op(neg), [[number(1)]]]]], [op(ln), [[number(2)]]]]] ;
    Ls = [op(add), [[op(sub), [[number(0)], [op(neg), [[op(ln), [[number(1)]]]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[op(neg), [[number(0)]]], [number(1)]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[op(neg), [[number(0)]]], [number(1)]]], [op(ln), [[number(2)]]]]] ;
    Ls = [op(add), [[op(sub), [[op(neg), [[number(0)]]], [op(ln), [[number(1)]]]]], [number(2)]]] ;
    Ls = [op(add), [[op(sub), [[op(neg), [[op(ln), [[number(0)]]]]], [number(1)]]], [number(2)]]] ;
    true.
    

    ?- e([neg,ln],[add,sub],[[number(0)],[number(1)],[number(2)]],Es,_);true.
    Es = t ;
    Es = u(t) ;
    Es = u(u(t)) ;
    Es = u(u(b(t, t))) ;
    Es = u(u(b(t, b(t, t)))) ;
    Es = u(u(b(b(t, t), t))) ;
    Es = u(b(t, t)) ;
    Es = u(b(t, u(t))) ;
    Es = u(b(t, u(b(t, t)))) ;
    Es = u(b(t, b(t, t))) ;
    Es = u(b(t, b(t, u(t)))) ;
    Es = u(b(t, b(u(t), t))) ;
    Es = u(b(u(t), t)) ;
    Es = u(b(u(t), b(t, t))) ;
    Es = u(b(u(b(t, t)), t)) ;
    Es = u(b(b(t, t), t)) ;
    Es = u(b(b(t, t), u(t))) ;
    Es = u(b(b(t, u(t)), t)) ;
    Es = u(b(b(u(t), t), t)) ;
    Es = b(t, t) ;
    Es = b(t, u(t)) ;
    Es = b(t, u(u(t))) ;
    Es = b(t, u(u(b(t, t)))) ;
    Es = b(t, u(b(t, t))) ;
    Es = b(t, u(b(t, u(t)))) ;
    Es = b(t, u(b(u(t), t))) ;
    Es = b(t, b(t, t)) ;
    Es = b(t, b(t, u(t))) ;
    Es = b(t, b(t, u(u(t)))) ;
    Es = b(t, b(u(t), t)) ;
    Es = b(t, b(u(t), u(t))) ;
    Es = b(t, b(u(u(t)), t)) ;
    Es = b(u(t), t) ;
    Es = b(u(t), u(t)) ;
    Es = b(u(t), u(b(t, t))) ;
    Es = b(u(t), b(t, t)) ;
    Es = b(u(t), b(t, u(t))) ;
    Es = b(u(t), b(u(t), t)) ;
    Es = b(u(u(t)), t) ;
    Es = b(u(u(t)), b(t, t)) ;
    Es = b(u(u(b(t, t))), t) ;
    Es = b(u(b(t, t)), t) ;
    Es = b(u(b(t, t)), u(t)) ;
    Es = b(u(b(t, u(t))), t) ;
    Es = b(u(b(u(t), t)), t) ;
    Es = b(b(t, t), t) ;
    Es = b(b(t, t), u(t)) ;
    Es = b(b(t, t), u(u(t))) ;
    Es = b(b(t, u(t)), t) ;
    Es = b(b(t, u(t)), u(t)) ;
    Es = b(b(t, u(u(t))), t) ;
    Es = b(b(u(t), t), t) ;
    Es = b(b(u(t), t), u(t)) ;
    Es = b(b(u(t), u(t)), t) ;
    Es = b(b(u(u(t)), t), t) ;
    true.
    

    无论哪种方式都有用,我只是因为与使用它们的项目相关的原因而从约束中生成的个人偏好。

    接下来的回忆是回到马特的回答。

    e([number(0)]           , t1        ) --> [].
    e([number(1)]           , t2        ) --> [].
    e([number(2)]           , t3        ) --> [].
    e([op(neg),[Arg]]       , u1(E)     ) --> [_],   e(Arg,E).
    e([op(ln),[Arg]]        , u2(E)     ) --> [_],   e(Arg,E).
    e([op(add),[Left,Right]], b1(E0,E1) ) --> [_,_], e(Left,E0), e(Right,E1).
    e([op(sub),[Left,Right]], b2(E0,E1) ) --> [_,_], e(Left,E0), e(Right,E1).
    
    e(EL,Es) :-
        length(Ls, _), phrase(e(EL,Es), Ls).
    
    es_count(M, Count) :-
        length([_|Ls], M),
        findall(., phrase(e(_,_), Ls), Sols),
        length(Sols, Count).
    

    我不会显示结果或详细解释这一点,因为此时它应该是微不足道的。值得注意的是,它会生成两种不同类型的结果,第一种是列表,第二种是复合词。

    原创5个问题

    最初的问题有5个部分,但不是为该答案创建新问题,而是删除了这个问题的部分内容,以便潜伏者提供的answer可以留在这里。

    1. CLP(FD)可以与此解决方案一起使用吗?如果可以,如何使用?
    2. 何时使用长度/ 2来约束DCG结果的大小以及何时可以使用CLP(FD)?
    3. 还有哪些其他方法可以引起DCG的迭代深化?
    4. 如何将非DCG解决方案转换回DCG版本?
    5. 随着我的DCG越来越复杂,我将需要更多的约束变量。是否有关于如何处理此问题的标准做法,或仅遵循rinse and repeat方法?

1 个答案:

答案 0 :(得分:3)

带计数器的基本树表达式解析器

假设二元一元树的复合术语表示(例如b(t,u(b(t,t,)))),这里是一个基本的解析器。通常建议使用CLP(FD)来推理整数。

expression(U, B, E) :-
    terminal(U, B, E).
expression(U, B, E) :-
    unary(U, B, E).
expression(U, B, E) :-
    binary(U, B, E).

terminal(0, 0, t).

unary(U, B, u(E)) :-
    U1 #>= 0,
    U #= U1 + 1,
    expression(U1, B, E).

binary(U, B, b(E1,E2)) :-
    U1 #>= 0, U2 #>= 0,
    U #= U1 + U2,
    B1 #>= 0, B2 #>= 0,
    B #= B1 + B2 + 1,
    expression(U1, B1, E1),
    expression(U2, B2, E2).

我在这里故意做了几件事。一种是使用CLP(FD)给出关于一元和二元项的计数的更多关系推理。我做的另一件事是首先使用更简单的expression/3子句,它不会进行递归。这样,Prolog将在探索可能的解决方案的过程中首先打击终端。

示例执行:

| ?- expression(1,2,E).

E = u(b(t,b(t,t))) ? a

E = u(b(b(t,t),t))

E = b(t,u(b(t,t)))

E = b(t,b(t,u(t)))

E = b(t,b(u(t),t))

E = b(u(t),b(t,t))

E = b(u(b(t,t)),t)

E = b(b(t,t),u(t))

E = b(b(t,u(t)),t)

E = b(b(u(t),t),t)

(1 ms) no


| ?- expression(U, B, E).

B = 0
E = t
U = 0 ? ;

B = 0
E = u(t)
U = 1 ? ;

B = 0
E = u(u(t))
U = 2 ? ;
...

使用DCG进行顺序表示

DCG用于解析序列。复合项可以解析为一系列标记或字符,可以通过使用DCG将其映射到复合项本身。例如,我们可能将复合树术语b(t,u(b(t,t)))表示为[b, '(', t, u, '(', b, '(', t, t, ')', ')', ')']。然后我们可以使用DCG并包含该表示。这是一个用这种序列格式反映上述实现的DCG:

expression(U, B, E) -->
    terminal(U, B, E) |
    unary(U, B, E) |
    binary(U, B, E).

terminal(0, 0, t) --> [t].

unary(U, B, u(E)) -->
    [u, '('],
    { U1 #>= 0, U #= U1 + 1 },
    expression(U1, B, E),
    [')'].

binary(U, B, b(E1, E2)) -->
    [b, '('],
    { U1 #>= 0, U2 #>= 0, U #= U1 + U2, B1 #>= 0, B2 #>= 0, B #= B1 + B2 + 1 },
    expression(U1, B1, E1),
    expression(U2, B2, E2),
    [')'].

同样,我将terminal//3作为expression//3的第一个查询过程。您可以看到此与非DCG版本之间的并行性。以下是示例执行。

| ?-  phrase(expression(1,2,E), S).

E = u(b(t,b(t,t)))
S = [u,'(',b,'(',t,b,'(',t,t,')',')',')'] ? a

E = u(b(b(t,t),t))
S = [u,'(',b,'(',b,'(',t,t,')',t,')',')']

E = b(t,u(b(t,t)))
S = [b,'(',t,u,'(',b,'(',t,t,')',')',')']

E = b(t,b(t,u(t)))
S = [b,'(',t,b,'(',t,u,'(',t,')',')',')']

E = b(t,b(u(t),t))
S = [b,'(',t,b,'(',u,'(',t,')',t,')',')']

E = b(u(t),b(t,t))
S = [b,'(',u,'(',t,')',b,'(',t,t,')',')']

E = b(u(b(t,t)),t)
S = [b,'(',u,'(',b,'(',t,t,')',')',t,')']

E = b(b(t,t),u(t))
S = [b,'(',b,'(',t,t,')',u,'(',t,')',')']

E = b(b(t,u(t)),t)
S = [b,'(',b,'(',t,u,'(',t,')',')',t,')']

E = b(b(u(t),t),t)
S = [b,'(',b,'(',u,'(',t,')',t,')',t,')']

no

| ?-  phrase(expression(U,B,E), S).

B = 0
E = t
S = [t]
U = 0 ? ;

B = 0
E = u(t)
S = [u,'(',t,')']
U = 1 ? ;

B = 0
E = u(u(t))
S = [u,'(',u,'(',t,')',')']
U = 2 ?
...

希望这能回答问题#1,也许#4举例。但是,将任何谓词集转换为DCG的一般问题更加困难。正如我上面提到的,DCG实际上是用于处理序列。

使用length/2来控制解决方案订单

在回答#2时,既然我们有一个能够正确生成解决方案的DCG解决方案,我们可以通过使用length/2来控制解决方案的顺序,这将按照长度而不是深度的顺序提供解决方案 - 第一。您可以从头开始约束长度,这比在递归中的每一步约束长度更有效和高效,这是多余的:

?- length(S, _), phrase(expression(U,B,E), S).

B = 0
E = t
S = [t]
U = 0 ? ;

B = 0
E = u(t)
S = [u,'(',t,')']
U = 1 ? ;

B = 1
E = b(t,t)
S = [b,'(',t,t,')']
U = 0 ? ;

B = 0
E = u(u(t))
S = [u,'(',u,'(',t,')',')']
U = 2 ? ;

B = 1
E = u(b(t,t))
S = [u,'(',b,'(',t,t,')',')']
U = 1 ? ;

B = 1
E = b(t,u(t))
S = [b,'(',t,u,'(',t,')',')']
U = 1 ? ;

B = 1
E = b(u(t),t)
S = [b,'(',u,'(',t,')',t,')']
U = 1 ? 
...

如果我使用一元二叉树的顺序表示来约束解,而不是解析,我会删除括号,因为它们在表示中是不必要的:

unary(U, B, u(E)) -->
    [u],
    { U1 #>= 0, U #= U1 + 1 },
    expression(U1, B, E).

binary(U, B, b(E1, E2)) -->
    [b],
    { U1 #>= 0, U2 #>= 0, U #= U1 + U2, B1 #>= 0, B2 #>= 0, B #= B1 + B2 + 1 },
    expression(U1, B1, E1),
    expression(U2, B2, E2).

由于列表长度与无效序列相对应的数量较少,因此效率可能更高一些。这导致:

| ?- length(S, _), phrase(expression(U, B, E), S).

B = 0
E = t
S = [t]
U = 0 ? ;

B = 0
E = u(t)
S = [u,t]
U = 1 ? ;

B = 0
E = u(u(t))
S = [u,u,t]
U = 2 ? ;

B = 1
E = b(t,t)
S = [b,t,t]
U = 0 ? ;

B = 0
E = u(u(u(t)))
S = [u,u,u,t]
U = 3 ? ;

B = 1
E = u(b(t,t))
S = [u,b,t,t]
U = 1 ? ;

B = 1
E = b(t,u(t))
S = [b,t,u,t]
U = 1 ? ;

B = 1
E = b(u(t),t)
S = [b,u,t,t]
U = 1 ? ;

B = 0
E = u(u(u(u(t))))
S = [u,u,u,u,t]
U = 4 ? ;

B = 1
E = u(u(b(t,t)))
S = [u,u,b,t,t]
U = 2 ? ;
...

所以,如果你有一个通用术语的递归定义,Term,它可以表示为一个序列(因此,使用DCG),那么length/2可以这样使用约束解决方案并按序列长度对它们进行排序,这对应于原始术语的某些排序。实际上,length/2的引入可能会阻止你的DCG在没有提出任何解决方案的情况下无限递归,但我仍然希望通过尝试组织逻辑首先走路终端来使DCG更好地开始行动。