目标是编写一个解析树生成器,该生成器将string类型的算术表达式作为输入并输出解析树。在下面提供的代码中,我们可以看到三种相互递归的方法expr(),term(),primary()。 expr()必须通过遍历输入的算术表达式字符串来返回解析树。解析规则由Exp-> Term | {+ Term},Term-> Primary * Primary,Primary-> a | b | c ... | z |(Exp)定义。如果仅使用+,则代码可以生成正确的解析树。例如,使用“ a + b”之类的输入字符串,代码产生Exp('+',Var a,Var b)。该代码的表达式失败,带有多个+。例如,a + b + c给出Exp('+',Var a,Var b),但实际上应该是Exp('+',Var a,Exp('+',Var b,Var c)。>
exception NotImplemented
type exptree = Var of char | Expr of char * exptree * exptree
let charSet =['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm'; 'n'; 'o';
'p'; 'q'; 'r'; 's'; 't'; 'u'; 'v'; 'w'; 'x'; 'y'; 'z']
let rec isin charr charlist =
match charlist with
| []-> false
|q::w -> if q=charr then true else isin charr w
let parse (inputexp: string): exptree =
let sym = ref inputexp.[0] in
let cursor = ref 0 in
let getsym () =
cursor := !cursor + 1;
sym := inputexp.[!cursor]
in
let rec expr (): exptree =
let p = term() in
match !sym with
| '+' -> (getsym(); Expr ('+',p,term()))
| _ -> p
and term (): exptree =
let p = primary() in
match !sym with
| '*' -> getsym() ;Expr ('*',p,primary())
| _ -> p
and primary (): exptree =
if !sym = '('
then begin
getsym ();
let result = expr () in
if !sym <> ')' then
failwith "Mismatched parens"
else if !cursor = (String.length inputexp) - 1 then
result
else begin
getsym ();
result
end
end
else
if isin !sym charSet then
if !cursor = (String.length inputexp) - 1 then
Var !sym
else
let result = Var !sym in
begin
getsym ();
result
end
else
failwith "In primary"
in
expr ()
因此,这表明我们有一个问题,即expr不会超出输入字符串中的第一个+。虽然使用while循环似乎很有希望。但是,递归调用在看到第一个+而不是寻找下一个+之后返回一个分析树。因此,请帮助这个人解决这个问题。
答案 0 :(得分:0)
如果要从while
循环返回值,可以使用异常。这是示例,该示例使用while true
循环,并使用将返回值作为有效负载的异常退出循环
exception Finished of int
let compute m =
let n = ref 0 in
let r = ref 1 in
try
while true do
if !n < m
then begin
r := !r + !r;
incr n;
end
else
raise_notrace (Finished !r)
done;
assert false
with Finished x -> x;;
请注意,我们必须添加assert false
语句以告知编译器此代码行不可访问。如果您的循环不是while true
循环,并且可以正常终止或异常终止,那么它将如下循环:
let compute m =
let n = ref 0 in
let r = ref 1 in
try
while !n < m do
incr n;
r := !r + !r;
if !r > 4096 then raise_notrace (Finished !r)
done;
!r
with Finished x -> x;;
注意,这当然是OCaml的非惯用代码,我们更喜欢使用递归来实现递归算法。此外,与其他语言不同,OCaml提供了非常廉价的异常,因此调用异常与在C语言中使用goto
一样便宜。我们使用raise_notrace
运算符在不记录回溯的情况下引发异常,以确保这一点。