如何正确使用whileloop返回afterwhile值

时间:2019-03-23 17:15:18

标签: ocaml

目标是编写一个解析树生成器,该生成器将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循环似乎很有希望。但是,递归调用在看到第一个+而不是寻找下一个+之后返回一个分析树。因此,请帮助这个人解决这个问题。

1 个答案:

答案 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运算符在不记录回溯的情况下引发异常,以确保这一点。