我正在编写一个函数解析器,它解析了这种函数:
<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
但它听起来不是正确的方法,也不是递归的!
解析字符串的正确方法是什么?提前谢谢!
答案 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结构尽可能接近,但这可能会引起不适。
无论如何,我希望这有帮助。