Prolog - 将迭代算法转换为递归(函数解析器)

时间:2017-02-04 00:30:43

标签: parsing prolog

我正在编写一个函数解析器,它解析了这种函数:

<fsignature>"(" <term1>, <term2> ... <termn>")"

在检查语言是否接受字符串之前,我将其转换为列表,然后将其发送到DCG规则。 我为此写了这个:

is_funct(A) :- term_to_atom(A, X), atom_chars(X, L), phrase(expr, L).

问题是,代码是非常残酷的,只是将每个字符块放入列表中,所以如果签名或文字长于一个字符,或者如果有嵌套函数,则它不起作用。 我认为正确的分块算法是:

List L, Z;
String F;

for(int i=0; i<L.length; i++){
  if(L[i]=="(" || L[i]==","){
    for(int j=i; L[j]!=")"||L[j]!=","; j++)
      F+=L[j];
    Z.append(F);
    if(Z[j-1]==")") Z.append(")");
  }
}

或者,滚动列表直到找到逗号或开括号,然后向后scoll并为找到的每个字符创建一个字符串,直到找到另一个括号或逗号,并且当您找到将字符串附加到列表时,如果你发现一个支架也附加它。所以你会得到像

这样的东西
[foo, "(", bar, fizz, "(", buzz, ")", hello, ")"]

要正确地查看它是否被语言接受。

我究竟如何在Prolog中递归翻译此算法? 我尝试设想一个解决方案,那就是

1) Split term at every comma (,) and put splitted strings in L
2) Find index of every bracket for ever element of L and split on the brackets
3) Reinsert brackets into list at the indexes saved

但它听起来不是正确的方法,也不是递归的!

解析字符串的正确方法是什么?提前谢谢!

1 个答案:

答案 0 :(得分:2)

以下是我根据自己的想法解决问题的方法。对于我认为错误做法的技术细节,你的问题很重要,所以我不完全确定这是正确的方向,但这是我得到的。

首先,我认为你的语法真的像这样:

function ::= name '(' termlist ')'.
termlist ::= [] | nonemptytermlist.
nonemptytermlist ::= term | term ',' nonemptytermlist.
term     ::= name
          |  function.
name     ::= [A-Za-z][A-Za-z0-9_-]*

我们在这里做Prolog,所以最多&#34;声明&#34;阅读您可以提出的问题是您想要编码的问题。只有在那之后你才想尝试优化它。 BNF语法在Prolog中很常见,语言内置了对它们的支持:明确的子句语法。

这个语法中有递归,但只要它处于正确的位置,通常不,语法就不是一个大问题。第一或最左边的位置。这是EBNF-ish,它应该很容易转换为DCG表示法。

function --> fname, "(", termlist, ")".
termlist --> [] | nonemptytermlist.
nonemptytermlist --> term | term, ",", nonemptytermlist.
term    --> fname | function.
fname    --> [C], { char_type(C, alpha) }, namebody.
namebody --> [C], { char_type(C, alnum) ; C = '_' ; C = '-' }, namebody.
namebody --> [].

这实际上似乎有效,但它并不是非常有用:

?- atom_codes("foo(this,bar(that),another)", X), phrase(function, X).
X = [102, 111, 111, 40, 116, 104, 105, 115, 44|...] ;

这里可能并不明显,但它已成功使用function DCG规则解析了这句话。你只是没有得到任何回报。接下来要做的是让你的语法规则构建你想要的结构。

function(F) -->
    fname(Name),
    "(", termlist(List), ")",
    { F =.. [Name|List] }.

termlist([])   --> [].
termlist(List) --> nonemptytermlist(List).

nonemptytermlist([X])    --> term(X).
nonemptytermlist([X|Xs]) --> term(X), ",", nonemptytermlist(Xs).

term(Term)     --> fname(Term).
term(Function) --> function(Function).

fname(Name)    --> [C], { char_type(C, alpha) }, namebody(Cs),
    { atom_codes(Name, [C|Cs]) }.

namebody([C|Cs]) -->
    [C],
    { char_type(C, alnum) ; C = '_' ; C = '-' },
    namebody(Cs).
namebody([]) --> [].

我们在这里完成的只是轻微重构事物,并通过每个规则解析的DCG规则参数值传回。现在您可以看到这成功解析了复杂的结构:

?- atom_codes("foo(this,bar(qwerty,uiop),that(),little())", X), phrase(function(F), X).
X = [102, 111, 111, 40, 116, 104, 105, 115, 44|...],
F = foo(this, bar(qwerty, uiop), that, little)

语法已成功将字符串转换为Prolog术语。遗憾的是,Prolog并没有多少指向foo()所以这些括号已被删除。这是由于&#34; univ&#34;我们用于将函数名和参数列表转换为Prolog结构的operator =..。可能是Prolog结构不容易处理的情况;在这种情况下,删除&#34; univ&#34;踩function就像这样:

function([Name|List]) -->
    fname(Name),
    "(", termlist(List), ")".

使用它会返回:

?- atom_codes("foo(this,bar(qwerty,uiop),that(),little())", X), phrase(function(F), X).
X = [102, 111, 111, 40, 116, 104, 105, 115, 44|...],
F = [foo, this, [bar, qwerty, uiop], [that], [little]] ;
false.

您仍然无法将术语与空函数区分开来。您可以通过更明确地term//1来解决这个问题:

function(Name, Args) -->
    fname(Name),
    "(", termlist(Args), ")".
% ...
term(term(Term))           --> fname(Term).
term(function(Name, Args)) --> function(Name, Args).

效果更加冗长:

?- atom_codes("foo(this,bar(qwerty,uiop),that(),little())", X), phrase(function(F,A), X).
X = [102, 111, 111, 40, 116, 104, 105, 115, 44|...],
F = foo,
A = [term(this), function(bar, [term(qwerty), term(uiop)]), function(that, []), function(little, [])]

这可能更容易或更难处理。我的经验法则是尽量保持与Prolog结构尽可能接近,但这可能会引起不适。

无论如何,我希望这有帮助。