簡體   English   中英

在沒有削減的情況下解析Prolog?

[英]Parsing in Prolog without cut?

我發現這個很好的片段用於解析Prolog中的lisp(從這里開始 ):

ws --> [W], { code_type(W, space) }, ws.
ws --> [].

parse(String, Expr) :- phrase(expressions(Expr), String).

expressions([E|Es]) -->
    ws, expression(E), ws,
    !, % single solution: longest input match
    expressions(Es).
expressions([]) --> [].

% A number N is represented as n(N), a symbol S as s(S).

expression(s(A))         --> symbol(Cs), { atom_codes(A, Cs) }.
expression(n(N))         --> number(Cs), { number_codes(N, Cs) }.
expression(List)         --> "(", expressions(List), ")".
expression([s(quote),Q]) --> "'", expression(Q).

number([D|Ds]) --> digit(D), number(Ds).
number([D])    --> digit(D).

digit(D) --> [D], { code_type(D, digit) }.

symbol([A|As]) -->
    [A],
    { memberchk(A, "+/-*><=") ; code_type(A, alpha) },
    symbolr(As).

symbolr([A|As]) -->
    [A],
    { memberchk(A, "+/-*><=") ; code_type(A, alnum) },
    symbolr(As).
symbolr([]) --> [].

但是表達式使用了切割。 我假設這是為了提高效率。 是否可以編寫此代碼,以便在不切割的情況下高效工作?

也會有感興趣的答案涉及Mercury的軟切/承諾選擇。

你在這里觸及一個非常深刻的問題。 在剪切的地方你添加了評論“最長的輸入匹配”。 但你實際上做的是提交第一個解決方案,它將產生非終端ws//0的“最長輸入匹配”,但不一定是expression//1

許多編程語言基於最長輸入匹配來定義其令牌。 這通常會導致非常奇怪的效果。 例如,一個數字后面可能會緊跟許多編程語言中的字母。 Pascal,Haskell,Prolog和許多其他語言就屬於這種情況。 例如, if a>2then 1 else 2是有效的Haskell。 有效的Prolog: X is 2mod 3.

鑒於此,定義一種編程語言可能是一個好主意,因此它根本不依賴於這些特性。

當然,您希望優化語法。 但我只能建議首先明確一個明確的定義。

至於效率(和純度):

eos([],[]).

nows --> call(eos).
nows, [W] --> [W], { code_type(W, nospace) }.

ws --> nows.
ws --> [W], {code_type(W, space)}, ws.

剪切不是用於提高效率,而是用於提交第一個解決方案(請參閱!/ 0旁邊的注釋:“單一解決方案:最長輸入匹配”)。 如果你注釋掉!/ 0,你可以得到例如:

?- parse("abc", E).
E = [s(abc)] ;
E = [s(ab), s(c)] ;
E = [s(a), s(bc)] ;
E = [s(a), s(b), s(c)] ;
false.

很明顯,在這種情況下,只需要由形成令牌的最長字符序列組成的第一種解決方案。 鑒於上面的例子,我因此不同意“假”:表達式// 1是不明確的,因為數字// 1和符號// 1是。 在Mercury中,您可以使用確定性聲明cc_nondet提交解決方案(如果有)。

您可以使用已在Parsing Expression Grammars(PEG)中找到其位置的構造,但也可在DCG中使用。 即對DCG目標的否定。 在PEG中,帶有參數的感嘆號(!)用於否定,即! 在DCG中,DCG目標的否定由(\\ +)運算符表示,該運算符已經被普通否定用作普通Prolog子句和查詢中的失敗。

首先讓我們解釋一下(\\ +)在DCG中是如何工作的。 如果您有以下形式的生產規則:

 A --> B, \+C, D.

然后這被翻譯為:

 A(I,O) :- B(I,X), \+ C(X,_), D(X,O).

這意味着嘗試解析C DCG目標,但實際上沒有消耗輸入列表。 現在,如果需要,這可以用來代替切割,並且它給出了一點聲明的感覺。 為了解釋這個想法,我們假設有一個沒有ws // 0的語法。 所以表達式// 1的原始子句集合將是:

expressions([E|Es]) --> expression(E), !, expressions(Es).
expressions([]) --> [].

通過否定,我們可以將其轉換為以下無格式形式:

expressions([E|Es]) --> expression(E), expressions(Es).
expressions([]) --> \+ expression(_).

遺憾的是,上述變體非常無效,因為嘗試解析表達式兩次。 一旦進入第一條規則,然后再進入第二條規則進行否定。 但是你可以做以下事情,只檢查表達式開頭的否定:

expressions([E|Es]) --> expression(E), expressions(Es).
expressions([]) --> \+ symbol(_), \+ number(_), \+ "(", \+ "'".

如果你嘗試否定,你會發現你得到一個相對嚴格的解析器。 如果您嘗試解析輸入的最大前綴以及是否要檢測某些錯誤,這一點很重要。 試試看:

?- phrase(expressions(X),"'",Y).

您應該在否定版本中檢查表達式的第一個符號。 在剪切和剪切免費版本中,您將獲得空列表的成功。

但是你也可以用另一種方式處理錯誤,我只是做了一個錯誤的例子,突出了一下否定版本的工作原理。

在其他設置中,例如CYK解析器,可以使得否定非常有效,它可以使用已經放置在圖表中的信息。

最好的祝福

暫無
暫無

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

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