如何在解析文本时传递State(并在需要时更改它)!?
https://www.metalevel.at/prolog/dcg
这个例子是数数。。
不知道我应该如何通过初始状态。我必须作为调用参数还是作为列表中的第一个元素来执行它?每当我想使用State时,我是否必须将State(S)作为第一个目标?
======
假设我必须解析字符串";添加项目5";。
如果状态是"0";"列表",比方说它应该打印";5=>列表";
如果";设置";那么";5=>设置";
或者可以是更复杂的字符串";新列表。加5"。。。其中解析";新列表";应该设置State=list,以便解析下一部分了解上下文,并且解释是";将5添加到列表中";vs";一组加5";。
无需使用这些示例,我只是想弄清楚何时使用半上下文表示法,以及如何在第一个/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,V], {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,DictWithCounts).
DictWithCounts = counts{a:3,b:4,dropped:2} ;
false.
?-
count_with_recursion(abgdbabba,DictWithCounts).
DictWithCounts = counts{a:3,b:4,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,b,c]),
!,
add_it(Key,DictIn,DictOut).
inc_for_key(Key,DictIn,DictOut) :-
+memberchk(Key,[a,b,c]),
add_it(dropped,DictIn,DictOut).
add_it(Key,DictIn,DictOut) :-
(get_dict(Key,DictIn,Count) -> succ(Count,CountNew) ; CountNew=1),
put_dict(Key,DictIn,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,DictIn,Dict2) }, dcg_count(Dict2,DictOut).
count_with_phrase(Atom,DictWithCounts) :-
atom_chars(Atom,Chars),
phrase(dcg_count(counts{},DictWithCounts),Chars).
% ===
% Using standard Prolog to count
% ===
count_with_recursion(Atom,DictWithCounts) :-
atom_chars(Atom,Chars),
count_rec(Chars,counts{},DictWithCounts).
count_rec([],Dict,Dict).
count_rec([C|Cs],DictIn,DictOut) :- inc_for_key(C,DictIn,Dict2), count_rec(Cs,Dict2,DictOut).
使用plunit
测试:
:- begin_tests(counting).
test("count using foldl/4, #1", true(DictWithCounts == counts{a:3,b:4,dropped:2})) :-
count_with_foldl(abgdbabba,DictWithCounts).
test("count whith phrase/2, #1", [true(DictWithCounts == counts{a:3,b:4,dropped:2}),nondet]) :-
count_with_phrase(abgdbabba,DictWithCounts).
test("count whith recursion, #1", [true(DictWithCounts == counts{a:3,b:4,dropped:2})]) :-
count_with_recursion(abgdbabba,DictWithCounts).
:- end_tests(counting).
DCGs中的单个状态变量
在上文中;输入状态";在论证位置1处;输出状态";其最终在自变量位置2处返回。
dcg_count(DictIn,DictOut)
这完全类似于函数式编程,在函数式编程中,不可变的结构被传递到函数中并由函数返回。这里的结构是";相关的";谓词,这是看待事物的另一种方式(这种方式有时会与必然在时间上向前计算的机器的现实相冲突)。
请注意,在上面,状态变量是地面-无变量。
如果单个状态变量是";空单元格";在较大结构的叶上。较大的结构";生长在它的叶子上";,DCG填充空信元,但为下一个呼叫留下另一个空信元。
例如这里有一个DCG;打开列表";在其末端CCD_ 12。Fin
始终是一个未绑定的变量,应按以下规则填写:
用原子中的:
替换tag
:
collect(Fin) --> [], { Fin = [] }.
collect(Fin) --> [t,a,g], !, collect(Fin2), { Fin = [':'|Fin2] }.
collect(Fin) --> [C], collect(Fin2), { Fin = [C|Fin2] }.
collect(Atom,Collected) :-
atom_chars(Atom,Chars),
phrase(collect(Collected),Chars).
?- collect(xytagyx,Out).
Out = [x,y,:,y,x] ;
false.
传统上,DCG代码编写得更紧凑(这种方式也使其易于进行尾调用优化,这一点不容忽视):
collect([]) --> [].
collect([':'|Fin]) --> [t,a,g], !, collect(Fin).
collect([C|Fin]) --> [C], collect(Fin).
collect(Atom,Collected) :-
atom_chars(Atom,Chars),
phrase(collect(Collected),Chars).
在这种情况下,每个DCG调用只看到打开列表远端的空单元格,而Collectecd
表示它的开始:
Collected ----> [|]
/
x [|]
/
: ~empty cell~ <--- Fin
因此,当遇到y
时,规则
collect([C|Fin]) --> [C], collect(Fin).
只是在其Fin
上完成其部分并将列表增加1个listcell;打开列表";用于继续附加:
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).
在这里,生长在叶子上的数据结构不是一个列表,而是一个二叉树。