【发布时间】:2020-12-16 22:58:57
【问题描述】:
为了学习一些 Prolog(我正在使用 GNU Prolog)并了解它的解析能力,我首先编写了一个 Lisp(或 S 表达式,如果我准确地说)标记器,它给出了一组标记,例如['(', 'f', 'o', 'o', ')'] 应该产生 ['(', 'foo', ')']。它没有按预期工作,这就是我在这里的原因!我认为我的思维过程在我的伪代码中闪耀:
tokenize([current | rest], buffer, tokens):
if current is '(' or ')',
Tokenize the rest,
And the output will be the current token buffer,
Plus the parenthesis and the rest.
if current is ' ',
Tokenize the rest with a clean buffer,
And the output will be the buffer plus the rest.
if the tail is empty,
The output will be a one-element list containing the buffer.
otherwise,
Add the current character to the buffer,
And the output will be the rest tokenized, with a bigger buffer.
我把它翻译成 Prolog 是这样的:
tokenize([Char | Chars], Buffer, Tokens) :-
((Char = '(' ; Char = ')') ->
tokenize(Chars, '', Tail_Tokens),
Tokens is [Buffer, Char | Tail_Tokens];
Char = ' ' ->
tokenize(Chars, '', Tail_Tokens),
Tokens is [Buffer | Tail_Tokens];
Chars = [] -> Tokens is [Buffer];
atom_concat(Buffer, Char, New_Buffer),
tokenize(Chars, New_Buffer, Tokens)).
print_tokens([]) :- write('.').
print_tokens([T | N]) :- write(T), write(', '), print_tokens(N).
main :-
% tokenize(['(', 'f', 'o', 'o', '(', 'b', 'a', 'r', ')', 'b', 'a', 'z', ')'], '', Tokens),
tokenize(['(', 'f', 'o', 'o', ')'], '', Tokens),
print_tokens(Tokens).
运行结果时,如下所示:gprolog --consult-file lisp_parser.pl 它只是告诉我no。我跟踪了main,它给了我下面的堆栈跟踪。我不明白为什么 tokenize 会因为空壳而失败。我看到缓冲区是空的,因为它是用之前的')' 清除的,但是即使Tokens 在那个时间点是空的,Tokens 不会递归地累积更大的结果吗?有擅长Prolog的人可以在这里给我一些提示吗?
| ?- main.
no
| ?- trace.
The debugger will first creep -- showing everything (trace)
(1 ms) yes
{trace}
| ?- main.
1 1 Call: main ?
2 2 Call: tokenize(['(',f,o,o,')'],'',_353) ?
3 3 Call: tokenize([f,o,o,')'],'',_378) ?
4 4 Call: atom_concat('',f,_403) ?
4 4 Exit: atom_concat('',f,f) ?
5 4 Call: tokenize([o,o,')'],f,_429) ?
6 5 Call: atom_concat(f,o,_454) ?
6 5 Exit: atom_concat(f,o,fo) ?
7 5 Call: tokenize([o,')'],fo,_480) ?
8 6 Call: atom_concat(fo,o,_505) ?
8 6 Exit: atom_concat(fo,o,foo) ?
9 6 Call: tokenize([')'],foo,_531) ?
10 7 Call: tokenize([],'',_556) ?
10 7 Fail: tokenize([],'',_544) ?
9 6 Fail: tokenize([')'],foo,_519) ?
7 5 Fail: tokenize([o,')'],fo,_468) ?
5 4 Fail: tokenize([o,o,')'],f,_417) ?
3 3 Fail: tokenize([f,o,o,')'],'',_366) ?
2 2 Fail: tokenize(['(',f,o,o,')'],'',_341) ?
1 1 Fail: main ?
(1 ms) no
{trace}
| ?-
【问题讨论】:
-
您可能需要为此使用定句语法,它用于处理(字符或其他事物的)列表。但除此之外,这很奇怪:
Tokens is [Buffer, Char | Tail_Tokens];... is 是算术评估,这可能不是您想要的。你想要atom_concat(Buffer,Char,Token), Tokens = [Token|Tail_Tokens]吗? -
@DavidTonhofer 我在想,如果有括号,我会做这样的事情:
'a', 'b', '(', 'c'对于那个 sn-p,一旦标记器命中括号,它就会知道缓冲区 @ 987654336@ 是一个完整的令牌,它可以清除令牌缓冲区。那么Tokens将部分是['ab', '(', ...]。你能解释一下为什么我要把括号和ab放在一起吗?什么是算术评估?我是新手,所以你可能知道得更好。 -
如果您调用
is,那么is右侧的内容必须是算术表达式。如X is 4+5。但是你说Tokens is [Buffer, Char | Tail_Tokens]。应该是Tokens = [Buffer, Char | Tail_Tokens],统一就好。 -
除了将
is替换为=,您还必须为递归定义添加一个基本情况:tokenize([], _, []).
标签: recursion prolog tokenize s-expression