应用半上下文传递附加参数

Applying semicontext for passing additional arguments

这是之前 from Mat's

的后续问题

从此开始

e([number(0)]           , t1        , Uc0    , Uc0, Bc0    , Bc0) --> [].
e([number(1)]           , t2        , Uc0    , Uc0, Bc0    , Bc0) --> [].
e([number(2)]           , t3        , Uc0    , Uc0, Bc0    , Bc0) --> [].

e([op(neg),[Arg]]       , u1(E)     , [_|Uc0], Uc1, Bc0    , Bc1) --> 
    [_],   
    e(Arg , E , Uc0, Uc1, Bc0, Bc1).
e([op(ln),[Arg]]        , u2(E)     , [_|Uc0], Uc1, Bc0    , Bc1) --> 
    [_],   
    e(Arg , E , Uc0, Uc1, Bc0, Bc1).

e([op(add),[Left,Right]], b1(E0,E1) , Uc0    , Uc2, [_|Bc0], Bc2) --> 
    [_,_], 
    e(Left, E0, Uc0, Uc1, Bc0, Bc1), 
    e(Right, E1, Uc1, Uc2, Bc1, Bc2).
e([op(sub),[Left,Right]], b2(E0,E1) , Uc0    , Uc2, [_|Bc0], Bc2) --> 
    [_,_], 
    e(Left, E0, Uc0, Uc1, Bc0, Bc1), 
    e(Right, E1, Uc1, Uc2, Bc1, Bc2).

e(U,B,EL,Es) :-
    length(UL, U),
    length(BL, B),
    phrase(e(EL,Es,UL,[],BL,[]), _).

e(N,EL,Es) :-
    length([_|Ls], N),
    phrase(e(EL,Es,_,[],_,[]),Ls).

e_count(E, Count) :-
    length([_|Ls], E),
    findall(., phrase(e(_,_,_,[],_,[]), Ls), Sols),
    length(Sols, Count).

然后阅读

For one variable, use a list with a single element that contains just that variable. If you want to pass around more than one variable, use a list that contains a single term of the form f(...), capturing all variables you want to pass around. This is also well worth its own question.

进化成这样

e( f([number(0)], t1, Uc0, Uc0, Bc0, Bc0) ) --> [].
e( f([number(1)], t2, Uc0, Uc0, Bc0, Bc0) ) --> [].
e( f([number(2)], t3, Uc0, Uc0, Bc0, Bc0) ) --> [].

e( f([op(neg), [Arg]], u1(E), [_|Uc0], Uc1, Bc0, Bc1) ) --> 
    [_], 
    e( f(Arg, E, Uc0, Uc1, Bc0, Bc1) ).
e( f([op(ln) , [Arg]], u2(E), [_|Uc0], Uc1, Bc0, Bc1) ) --> 
    [_], 
    e( f(Arg, E, Uc0, Uc1, Bc0, Bc1) ).

e( f([op(add), [Left,Right]], b1(E0,E1) ,Uc0, Uc2, [_|Bc0], Bc2) ) --> 
    [_,_], 
    e(f(Left, E0, Uc0, Uc1, Bc0, Bc1) ), 
    e(f(Right, E1, Uc1, Uc2, Bc1, Bc2) ).
e( f([op(sub), [Left,Right]], b2(E0,E1) ,Uc0, Uc2, [_|Bc0], Bc2) ) --> 
    [_,_],
    e(f(Left, E0, Uc0, Uc1, Bc0, Bc1) ),
    e(f(Right, E1, Uc1, Uc2, Bc1, Bc2) ).

e(U,B,EL,Es) :-
    length(UL, U),
    length(BL, B),
    phrase(e(f(EL,Es,UL,[],BL,[])), _).

e_N(N,EL,Es) :-
    length([_|Ls], N),
    phrase(e(f(EL,Es,_,[],_,[])),Ls).

e_count(E, Count) :-
    length([_|Ls], E),
    findall(., phrase(e(f(_,_,_,[],_,[])), Ls), Sols),
    length(Sols, Count).

有效,但已注明 use a list that contains a single term of the form f(...),此处 f(...) 不在列表中。

我是不是哪里出错了?

补充

参考资料

带有隐式状态传递的变量命名约定

通常在使用 时变量被命名为

S0S1 →...→ S

但是对于我的一元-二叉树示例,我将它们命名为

S0S1 →...→ Sn

Sn 而不是 S 结尾。

例如

标准

e(S0,S) :-

这里

 e(S0,S2) :-

原因是这个例子很少有 属性 每个 DCG 谓词的长度增加,

例如

e([number(0)]           , t1        , Uc0    , Uc0, Bc0    , Bc0) -->  
e([op(neg),[Arg]]       , u1(E)     , [_|Uc0], Uc1, Bc0    , Bc1) -->   
e([op(add),[Left,Right]], b1(E0,E1) , Uc0    , Uc2, [_|Bc0], Bc2) -->  

所以以 xn 结尾让我再检查一次准确性。

参考:ISO/IEC DTR 13211–3:2006 Definite clause grammar rules

6.1.3 Variable names convention for terminal-sequences

This TR uses variables named S0, S1, ..., S to represent the terminal-sequences used as arguments when processing grammar rules or when expanding grammar rules into clauses. In this notation, the variables S0, S1, ..., S can be regarded as a sequence of states, with S0 representing the initial state and the variable S representing the final state. Thus, if the variable Si represents the terminal-sequence in a given state, the variable Si+1 will represent the remaining terminal-sequence after parsing Si with a grammar rule.

DCG 总是描述了一个列表

但是哪个列表? 您必须决定如何使用该机制

在上述情况下,似乎您已经使用 DCG 来描述一个列表,您将其长度用作衡量搜索深度。

这完全没问题,而且是对 DCG 的一种非常自然的使用。

但是,你只能描述一个列表,不能同时描述两个,至少不能用"primary"的方式。您当然可以通过 DCG arguments 同时描述任意多个术语。但是,DCG body 仅限于描述 one 列表,通过调用非终结符和使用终结符。

有一种 straight-forward 方法可以将 DCG 转换为常规 Prolog 代码 而无需 DCG,或者通过常规 Prolog 解释 DCG 规则。有关详细信息,请参阅 ISO 草案。

例如,让我们只看这个片段:

e( f([op(neg), [Arg]], u1(E), [_|Uc0], Uc1, Bc0, Bc1) ) --> 
    [_], 
    e( f(Arg, E, Uc0, Uc1, Bc0, Bc1) ).
e( f([op(ln) , [Arg]], u2(E), [_|Uc0], Uc1, Bc0, Bc1) ) --> 
    [_], 
    e( f(Arg, E, Uc0, Uc1, Bc0, Bc1) ).

我们去掉DCG语法,写成例如为:

e( f([op(neg), [Arg]], u1(E), [_|Uc0], Uc1, Bc0, Bc1, [_|Rest0], Rest) ) :- 
    e( f(Arg, E, Uc0, Uc1, Bc0, Bc1, Rest0, Rest) ).
e( f([op(ln) , [Arg]], u2(E), [_|Uc0], Uc1, Bc0, Bc1, [_|Rest0], Rest) ) :- 
    e( f(Arg, E, Uc0, Uc1, Bc0, Bc1, Rest0, Rest) ).

(当然请注意,您不应该依赖任何特定的扩展方法,而是始终使用phrase/2接口来调用DCG。不过,以上是一种 方式来执行这样的扩展,获得常规的 Prolog 代码。)

切换参数

假设我们想返回再次使用 DCG 表示法,因为它非常方便。例如,在使用 DCG 表示法时,我们显然需要考虑更少的变量,这本身就是一个巨大的优势。

因此,我们当然可以回到最初的 DCG,使用终端而不是我们引入的最后两个新参数来描述列表差异。

但我们也可以做其他事情!

例如,在上面的代码片段中,我们注意到 Bc0Bc1 仅通过 : None 子句真正关心这些争论。因此,假设我们使用 DCG 机制来描述这两个 参数。

例如,这可能如下所示:

e( f([op(neg), [Arg]], u1(E), [_|Uc0], Uc1, [_|Rest0], Rest) ) -->
    e( f(Arg, E, Uc0, Uc1, Rest0, Rest) ).
e( f([op(ln) , [Arg]], u2(E), [_|Uc0], Uc1, [_|Rest0], Rest) ) -->
    e( f(Arg, E, Uc0, Uc1, Rest0, Rest) ).

这里,我从普通的Prolog版本开始,引入DCG符号,简单地将Bc0Bc1变成了隐式参数!它们根本不再出现在这里。只有当我们再次将其扩展回 Prolog 时,它们才会变得可见,或者至少这是一种方法。

但是,还有两个问题:

首先,如果我们实际上想要访问这些参数怎么办?当然不是 all 子句只是像这样把它们串起来。我们还需要在某处访问它们。其次,还有一个更根本的问题:我们想讨论一个参数,它可以是 任何项 ,但 DCG 总是描述一个 list!

那么,我们如何调和这一切?

最重要的是,我们需要讨论列表,所以解决方案很简单:让我们讨论具有单个元素的列表,即列表[Bc0][Bc1]。这使得 DCG 符号在这种情况下完全适用。

接下来,我们如何表达DCG中Bc0Bc1之间的关系?为此,我们使用 半上下文表示法:它让我们可以讨论以前不在我们描述的列表中的元素。

如 DCG 入门中所述,以下形式的非终结符将很有用:

state(S0, S), [S] --> [S0].

您可以将非终结符 state(S0, S) 读作:当前状态为 S0此后S

因此,如果您想要实际访问 Bc0 并将其与您的其中一个子句中的 Bc1 相关联,您可以这样做:

e( f([op(neg), [Arg]], u1(E), [_|Uc0], Uc1, [_|Rest0], Rest) ) -->
    state(Bc0, Bc1),
    ... (something involving Bc0 and Bc1) ...
    e( f(Arg, E, Uc0, Uc1, Rest0, Rest) ).

主要优点是:此表示法允许您隐藏 附加参数,仍然 允许您显式访问它们如果你需要它,使用state//2(或直接使用半上下文符号)。

这显然变得越来越有吸引力在您的代码中实际使用的参数越少。如果几乎所有子句都访问参数,则隐藏它们是没有意义的。但是,您经常会描述贯穿其中的术语,而实际上只有极少数 DCG 子句会访问它们。在这种情况下,请考虑使用 DCG 表示法隐式传递它们。

但仍然存在一个问题:如果我们不仅要传递 一个 任期,而且要传递两个或更多任期,该怎么办?对此有一个非常简单的解决方案:让我们仍然传递一个包含单个元素的列表,但是让t 元素只是 形式为 f(Arg1,Arg2,...,Arg_n) 的复合词。因此,您对非终结符 e//N 的调用可能如下所示:

?- phrase(e(Arg1,Arg2,...,Arg_N), [f(B1,B2,...,B_M)], [Result]).

此处,B1B2 等是您希望隐式传递的参数,其形式为带有结合所有这些参数的单个元素。

假设这回答了您的问题,合适的标题可以是:"Applying semicontext notation for passing additional arguments"。实际问题很清楚,我认为这在这种情况下很关键,你想应用半上下文符号来传递额外的参数即使你已经专门使用 DCG 符号来描述其他东西 .总而言之,一个解决方案是首先释放 DCG 符号,以便您可以使用描述的列表来传递额外的参数。

请注意,还有其他解决方案:例如,您可以设计自己的 自定义表示法,完全类似于 DCG 表示法,但扩展方式可以让您同时描述两个独立的列表。我把它留作练习。