Parsing 解析文本时DCG中的线程状态/上下文

Parsing 解析文本时DCG中的线程状态/上下文,parsing,prolog,state,dcg,dcg-semicontext,Parsing,Prolog,State,Dcg,Dcg Semicontext,如何在解析文本时传递状态(并在需要时进行更改) 例如做计数 不知道我应该如何通过初始状态。我必须作为调用参数还是作为列表中的第一个元素来执行它? 每当我想使用状态时,我是否必须将状态作为第一个目标 ====== 假设我必须解析字符串“additem5” 如果状态为“list”,那么它应该打印“5=>list” 如果“设置”,则“5=>设置” 或者可能是更复杂的字符串“newlist.add5”。。。其中,解析“newlist”应设置State=list,以便解析下一部分时了解上下文,解释为“向

如何在解析文本时传递状态(并在需要时进行更改)

例如做计数

不知道我应该如何通过初始状态。我必须作为调用参数还是作为列表中的第一个元素来执行它? 每当我想使用状态时,我是否必须将状态作为第一个目标

======

假设我必须解析字符串“additem5”

如果状态为“list”,那么它应该打印“5=>list”

如果“设置”,则“5=>设置”

或者可能是更复杂的字符串“newlist.add5”。。。其中,解析“newlist”应设置State=list,以便解析下一部分时了解上下文,解释为“向列表添加5”vs“向集合添加5”

不需要使用这些示例,我只是想知道何时使用半上下文表示法,以及如何在第一个/最上面的规则中将上下文作为参数传递

正如你所看到的,我很困惑,但这是我在互联网上能找到的唯一一个例子,prolog文档总是密集、简洁且没有多大帮助;(没有例子


澄清:

sent([X,Y],Ctx) -->  make(X,Ctx), add(Y,Ctx).
make(V,Ctx) --> [make,V], {say(">make ~w |ctx: ~w", [V,Ctx])}.
add(V,_Ctx) --> [add,V], {say(">add ~w", [V])}.
在本例中,我在每个级别传递上下文,即使我没有在add()中使用它。 我可以为add()删除它,但如果有子规则,我必须保留它

我知道使用状态线程只需要在最重要的规则中声明Ctx,无论何时使用它,这个例子可能会有所帮助。我将暂时不使用半文本技巧,作为以后的讨论

我们要计算原子中出现的
a
b
c
的次数。任何其他字符都被归为
删除的
类别(但也被计数)

因此,“状态”是
a
b
c
删除的当前计数。应使用映射实现状态,在这种情况下,使用SWI Prolog dict

有三种方法:

  • 使用
    foldl/4
  • 短语/3
  • 使用香草Prolog
  • 代码如下:

    ?-
    count_with_foldl(abgdbabba,DictWithCounts).
    DictWithCounts = counts{a:3,b:4,dropped:2}.
    
    ?- 
    count_with_phrase(abgdbabba,DictWithCounts).
    DictWithCounts = counts{a:3,b:4,dropped:2} ;
    false.
    
    ?- 
    count_with_recursion(abgdbabba,DictWithCounts).
    DictWithCounts = counts{a:3,b:4,dropped:2}.
    
    代码:

    %===
    %将DictIn变形为DictOut,以便:
    %仅适用于键[a、b、c]:
    %若DictIn中存在键,则DictIn为DictIn,且键的计数增加
    %如果DictIn中存在键note,则DictIn将使用计数为1的新条目键
    % ===
    包括按键(按键、听写、听写):-
    成员CHK(键[a、b、c]),
    !,
    添加它(键、DictIn、DictOut)。
    包括按键(按键、听写、听写):-
    \+成员CHK(键[a、b、c]),
    添加它(删除、编辑、编辑)。
    添加它(键、读音、读音):-
    (get_dict(Key,DictIn,Count)->suck(Count,CountNew);CountNew=1),
    输入dict(键、DictIn、CountNew、DictOut)。
    % ===
    %使用foldl进行计数
    % ===
    用foldl计数(原子,DictWithCounts):-
    原子字符(原子,字符),
    foldl(inc_表示密钥、字符、计数{}、DictWithCounts)。
    % ===
    %使用DCG进行计数
    % ===
    dcg_计数(Dict,Dict)--->[]。
    dcg_计数(DictIn,DictOut)->[C],{inc_代表_键(C,DictIn,Dict2)},dcg_计数(Dict2,DictOut)。
    用短语计数(原子,DictWithCounts):-
    原子字符(原子,字符),
    短语(dcg_计数(计数{},DictWithCounts),字符)。
    % ===
    %使用标准Prolog进行计数
    % ===
    使用递归计数(Atom、DictWithCounts):-
    原子字符(原子,字符),
    count_rec(Chars,counts{},DictWithCounts)。
    计数记录([],记录,记录)。
    count|rec([C|Cs],DictIn,DictOut):-inc_代表_键(C,DictIn,Dict2),count|rec(Cs,Dict2,DictOut)。
    
    通过测试:

    :-开始测试(计数)。
    test(“使用foldl/4进行计数,#1”,true(DictWithCounts==计数{a:3,b:4,drop:2})):-
    用foldl计数(abgdbabba,DictWithCounts)。
    测试(“带短语/2的计数,#1”,[true(DictWithCounts==counts{a:3,b:4,drop:2}),非集]:-
    用短语计数(abgdbabba,DictWithCounts)。
    测试(“带递归的计数,#1”,[true(DictWithCounts==counts{a:3,b:4,drop:2})]:-
    使用递归计数(abgdbabba、DictWithCounts)。
    :-结束测试(计数)。
    
    DCGs中的单个状态变量

    在上面的例子中,DCG在参数位置1处采用“输入状态”,并将其发展为“输出状态”,最终在参数位置2处返回

    dcg_count(DictIn,DictOut)
    
    这完全类似于函数式编程,通过函数将不可变的结构传递到函数中并由函数返回。在这里,结构通过谓词“关联”,这是另一种看待事物的方式(这种方式有时会与必须在时间上向前计算的机器的实际情况发生冲突)

    请注意,上面的状态变量是地面-无变量

    如果状态变量是较大结构叶上“空单元格”的名称,则单个状态变量就足够了。较大结构“在其叶上生长”,DCG填充空单元格,但为下一次调用留下另一个空单元格

    例如,这里是一个DCG,它在其末尾增长一个“开放列表”
    Fin
    Fin
    始终是一个未绑定的变量,应该由规则填充:

    将原子中的
    标记替换为

    collect(Fin)--->[],{Fin=[]}。
    收集(Fin)->[t,a,g],!,收集(Fin2),{Fin=[':'| Fin2]}。
    collect(Fin)->[C],collect(Fin2),{Fin=[C|Fin2]}。
    收集(原子,收集):-
    原子字符(原子,字符),
    短语(collect(Collected),Chars)。
    
    ?-收集(xytagyx,Out)。
    Out=[x,y,:,y,x];
    错。
    
    传统上,DCG代码编写得更紧凑(这种方式也使其易于进行尾部调用优化,这一点不容忽视):

    collect([])-->[]。
    收集([':'| Fin])-->[t,a,g],!,收集(Fin)。
    收集([C|Fin])-->[C],收集(Fin)。
    收集(原子,收集):-
    原子字符(原子,字符),
    短语(collect(Collected),Chars)。
    
    在这种情况下,每个DCG调用只能看到开放列表远端的空单元格,而
    collectedcd
    表示其开始:

    已收集