3

这是Mat 的回答中的一个较早问题的后续问题

从此开始

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

然后阅读

对于一个变量,使用一个包含仅包含该变量的单个元素的列表。如果要传递多个变量,请使用包含 f(...) 形式的单个项的列表,捕获所有要传递的变量。这也很值得提出自己的问题。

演变成这样

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 定句语法规则

6.1.3 终端序列的变量名约定

此 TR 使用名为 S0、S1、...、S 的变量来表示在处理语法规则或将语法规则扩展为子句时用作参数的终端序列。在这种表示法中,变量 S0、S1、...、S 可以看作是一系列状态,其中 S0 代表初始状态,变量 S 代表最终状态。因此,如果变量 Si 表示给定状态下的终端序列,则变量 Si+1 将表示用语法规则解析 Si 后剩余的终端序列。

4

1 回答 1

2

DCG总是描述一个列表

但是哪个列表?你必须决定如何奉献这个机制

在上述情况下,您似乎已经有专门的 DCG 来描述您使用其长度作为搜索深度度量的列表。

这完全没问题,而且非常自然地使用 DCG。

但是,您只能描述一个列表,而不是同时描述两个,至少不能以“主要”方式。您当然可以通过 DCG参数同时描述任意多个术语 。但是,DCG 主体仅限于通过调用非终结符和使用终结符来描述一个列表。

有一种直接的方法可以将 DCG 转换为没有DCG 的常规 Prolog 代码,或者通过常规 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,使用终端而不是我们引入的用于描述列表差异的最后两个新参数。

但我们也可以做其他事情

例如,在上面的代码片段中,我们注意到Bc0andBc1通过: 没有一个子句真正关心这些参数。所以,假设我们用 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 时,它们才会变得可见,或者至少这是一种方法。

但是,仍然存在两个问题:

首先,如果我们真的想访问这些参数怎么办?当然,并非所有子句都像这样贯穿它们。我们还需要在某个地方访问它们。其次,还有一个更根本的问题:我们想讨论一个参数,它可以是任何术语,但 DCG 总是描述一个列表

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

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

接下来,我们如何表达DCG之间Bc0和内部的关系?Bc1为此,我们使用半上下文符号:它让我们谈论以前不在我们描述的列表中的元素。

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

状态(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) ,
    ...(涉及 Bc0 和 Bc1)...
    e( f(Arg, E, Uc0, Uc1, Rest0, Rest) )。

主要优点是:这种表示法可以让您隐藏附加参数,如果需要,仍然可以使用(或直接使用半上下文表示法)显式访问它们。state//2

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

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

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

这里,B1B2是你想隐式传递的参数,以列表的形式, 具有组合所有这些参数的单个元素。

假设这回答了您的问题,一个合适的标题可能是:“应用半上下文符号来传递附加参数”。实际问题很清楚,我认为在这种情况下很关键,即使您已经使用 DCG 表示法来描述其他内容,您也希望应用半上下文表示法来传递额外的参数。总而言之,一个解决方案是首先释放 DCG 符号,以便您可以使用描述的列表来传递其他参数。

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

于 2017-03-12T21:43:40.020 回答