簡體   English   中英

解析文本時在 DCG 中處理狀態/上下文

[英]Threading state/context in DCG while parsing text

解析文本時如何傳遞 State (並在需要時更改它)?

https://www.metalevel.at/prolog/dcg

該示例正在計數..

不知道我應該如何通過最初的 State。 我必須將其作為調用參數還是列表中的第一個元素? 每當我想使用 State 時,我是否必須將 state(S) 作為第一個目標?

======

假設我必須解析字符串“添加項目 5”。

如果 state 是“列表”,假設它應該打印“5 =>列表”

如果“設置”然后“5 =>設置”

或者可能是更復雜的字符串“new list.add 5”......其中解析“new list”應該設置State=list,以便解析下一部分知道上下文並且解釋是“add 5 to a list ” 與“將 5 加到一組中”。

無需使用這些示例,我只是想弄清楚何時使用半上下文表示法以及如何將上下文作為參數傳遞給 first/top 規則。

如您所見,我很困惑,但這是我在互聯網上能找到的唯一示例,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() 刪除它,但如果有子規則我必須保留它。

我知道使用 State 線程只需要在頂級規則中聲明 Ctx 並且無論何時使用它

DCG 是一種解析方法,因其與宿主語言(當然是 Prolog)的緊密和輕量級集成而引起了一些興趣。 確實如此輕量級,以至於 DCG 子句中包含的解析實際上沒有任何特定的內容,只有差異列表使(相當)有效的模式匹配成為可能。

如果使用 DCG 進行解析,則無法將 state 線程化到不同的子句中,因為差異列表用於匹配標記。

因此,使用傳統方式,在 arguments 中手動傳遞 state,或者切換到擴展 DCG - 例如,pack( edcg )。

也許這個例子有幫助。 我暫時不考慮半上下文技巧,作為稍后的討論。

我們想計算原子中存在的ab以及c的出現次數。 任何其他字符都歸入一個已dropped的類別(但也算在內)。

因此,“狀態”是abc的當前計數, dropped state 應使用 map 實現,在本例中為 SWI-Prolog 字典。

三種方法:

  1. foldl/4
  2. phrase/3
  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).

DCG 中的單個 state 變量

在上面,DCG 在參數 position 1 處采用“輸入狀態”,它們發展成“輸出狀態”,最終在參數 position 2 處返回。

dcg_count(DictIn,DictOut)

這完全類似於函數式編程,其中不可變結構被傳遞給函數並由函數返回。 在這里,結構通過謂詞“相關”,這是另一種看待事物的方式(這種方式有時會與必須及時向前計算的機器的現實相沖突)。

請注意,上面的 state 變量是接地的 - 沒有變量。

如果 state 變量是較大結構葉中“空單元”的名稱,則單個state 變量就足夠了。 較大的結構“在它的葉子上生長”,一個 DCG 填充空單元格,但為下一次調用留下另一個空單元格。

例如,這是一個 DCG,它在其末尾增長了一個“開放列表” Fin 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 個列表單元,但將其保留為“打開列表”以繼續附加:

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

在這里,在它的葉子上增長的數據結構不是一個列表,而是一個二叉樹。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM