如何解决解析文本时在 DCG 中处理状态/上下文
如何在解析文本时传递状态(并在需要时更改它)!?
https://www.metalevel.at/prolog/dcg
这个例子是在做计数..
不知道我应该如何通过初始状态。我是否必须将其作为调用参数或作为列表中的第一个元素? 每当我想使用 State 时,我是否必须将 state(S) 作为第一个目标?
======
假设我必须解析字符串“add item 5”。
如果状态是 "list" ,假设它应该打印 "5=>list"
如果“设置”则“5=>设置”
或者可能是更复杂的字符串“new list.add 5”...其中解析“new list”应该设置State=list,以便解析下一部分知道上下文并且解释为“add 5到列表中”与“将 5 个添加到一个集合中”。
无需使用这些示例,我只是想弄清楚何时使用半上下文表示法以及如何在 first/top 规则中将上下文作为参数传递。
正如你所看到的,我很困惑,但这是我能在互联网上找到的唯一示例,并且序言文档一如既往地密集、简洁且不是很有帮助;(没有示例。
澄清:
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,{say(">add ~w",[V])}.
在这个例子中,我在每个级别传递上下文,即使我没有在 add() 中使用它。 我可以为 add() 删除它,但如果有子规则,我必须保留它。
我知道使用状态线程只需要在顶级规则中声明 Ctx 并且在使用时声明
解决方法
DCG 是一种引起一些兴趣的解析方法,因为它们紧密而轻量级地集成到宿主语言 - 当然是 Prolog。确实如此轻量级,以至于实际上没有特定于包含在 DCG 子句中的解析,只有通过差异列表实现的(相当)有效的模式匹配。
如果使用 DCG 进行解析,则不能将状态串连到不同的子句中,因为差异列表用于匹配标记。
因此,请使用传统方式,在参数中手动传递状态,或切换到扩展 DCG - 例如,pack(edcg)。
,也许这个例子有帮助。 我暂时不讨论半上下文技巧,稍后再讨论。
我们想要计算原子中 a
和 b
和 c
的出现次数。任何其他字符都归入 dropped
类别(但也算在内)。
“状态”因此是 a
、b
、c
、dropped
的当前计数。状态应使用映射实现,在本例中为 SWI-Prolog dict。
三种方法:
- 用
foldl/4
- 用
phrase/3
- 使用原版 Prolog
使用以下代码:
?-
count_with_foldl(abgdbabba,DictWithCounts).
DictWithCounts = counts{a:3,b:4,dropped:2}.
?-
count_with_phrase(abgdbabba,dropped:2} ;
false.
?-
count_with_recursion(abgdbabba,dropped:2}.
代码:
% ===
% Morph DictIn to DictOut so that:
% Only for Keys [a,b,c]:
% If Key exists in DictIn,DictOut is DictIn with the count for Key incremented
% If Key notexists in DictIn,DictOut is DictIn with a new entry Key with count 1
% ===
inc_for_key(Key,DictIn,DictOut) :-
memberchk(Key,[a,c]),!,add_it(Key,DictOut).
inc_for_key(Key,DictOut) :-
\+memberchk(Key,add_it(dropped,DictOut).
add_it(Key,DictOut) :-
(get_dict(Key,Count) -> succ(Count,CountNew) ; CountNew=1),put_dict(Key,CountNew,DictOut).
% ===
% Using foldl to count
% ===
count_with_foldl(Atom,DictWithCounts) :-
atom_chars(Atom,Chars),foldl(inc_for_key,Chars,counts{},DictWithCounts).
% ===
% Using a DCG to count
% ===
dcg_count(Dict,Dict) --> [].
dcg_count(DictIn,DictOut) --> [C],{ inc_for_key(C,Dict2) },dcg_count(Dict2,DictOut).
count_with_phrase(Atom,phrase(dcg_count(counts{},DictWithCounts),Chars).
% ===
% Using standard Prolog to count
% ===
count_with_recursion(Atom,count_rec(Chars,DictWithCounts).
count_rec([],Dict,Dict).
count_rec([C|Cs],DictOut) :- inc_for_key(C,Dict2),count_rec(Cs,Dict2,DictOut).
使用 plunit
测试:
:- begin_tests(counting).
test("count using foldl/4,#1",true(DictWithCounts == counts{a:3,dropped:2})) :-
count_with_foldl(abgdbabba,DictWithCounts).
test("count whith phrase/2,[true(DictWithCounts == counts{a:3,dropped:2}),nondet]) :-
count_with_phrase(abgdbabba,DictWithCounts).
test("count whith recursion,dropped:2})]) :-
count_with_recursion(abgdbabba,DictWithCounts).
:- end_tests(counting).
DCG 中的单个状态变量
在上面,DCG 在参数位置 1 处采用“输入状态”,然后将其发展为“输出状态”,最终在参数位置 2 处返回。
dcg_count(DictIn,DictOut)
这完全类似于函数式编程,通过这种方式将不可变结构传递给函数并由函数返回。在这里,结构通过谓词“相关”,这是另一种看待事物的方式(这种方式有时会与必须及时向前计算的机器的现实相冲突)。
请注意,上面的状态变量是接地的 - 没有变量。
单个状态变量就足够了,如果该状态变量是较大结构叶处的“空单元”的名称。较大的结构“在其叶子上生长”,DCG 填充空单元格,但为下一次调用留下另一个空单元格。
例如,这里是一个 DCG,它在其末尾 Fin
增长了一个“开放列表”。 Fin
始终是一个未绑定的变量,应由规则填充:
将原子中的 tag
替换为 :
:
collect(Fin) --> [],{ Fin = [] }.
collect(Fin) --> [t,a,g],collect(Fin2),{ Fin = [':'|Fin2] }.
collect(Fin) --> [C],{ Fin = [C|Fin2] }.
collect(Atom,Collected) :-
atom_chars(Atom,phrase(collect(Collected),Chars).
?- collect(xytagyx,Out).
Out = [x,y,:,x] ;
false.
DCG 代码传统上写得更紧凑(这种方式也使其适合尾调用优化,这是不可忽视的):
collect([]) --> [].
collect([':'|Fin]) --> [t,collect(Fin).
collect([C|Fin]) --> [C],collect(Fin).
collect(Atom,Chars).
在这种情况下,每个 DCG 调用只能看到打开列表远端的空单元格,而 Collectecd
表示它的开始:
Collected ----> [|]
/ \
x [|]
/ \
: ~empty cell~ <--- Fin
所以,当遇到y
规则时
collect([C|Fin]) --> [C],collect(Fin).
只是在它的 Fin
上做它的一部分并将列表增加 1 个列表单元格,但将其保留为“开放列表”以继续追加:
Collected ----> [|]
/ \
x [|]
/ \
: [|]
/ \
C ----> y ~empty cell~ <--- Fin
规则
collect(Fin) --> [],{ Fin = [] }.
将打开的列表转换为适当的列表,将空单元格设置为[]
:
Collected ----> [|]
/ \
x [|]
/ \
: [|]
/ \
y []
这当然正是 DCG Primer 的树示例中发生的情况:
tree_nodes(nil) --> [].
tree_nodes(node(Name,Left,Right)) -->
tree_nodes(Left),[Name],tree_nodes(Right).
这里,在叶子上生长的数据结构不是列表,而是二叉树。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。