Skip to main content
edited tags
Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238
Source Link
Jay
  • 33
  • 3

Infix epression evaluation

I'm looking for some feedback on my OCaml implementation of some methods to translate infix expressions to postfix and then evaluate them.

I'm very new to OCaml, coming from C#/Java and JavaScript so pretty much any advice is appreciated, in particular how to make it more idiomatic or "functional".

It seems like I have to reverse my stack at some point because I want to operate on both sides, is this unavoidable? Could I be using better exceptions for my helper functions? Is it good practice to declare helper functions like I did? apply_and_store and str_to_fn could be inside eval_postfix, is it better to have them encapsulated or to keep eval_postfix cleaner?

let explode_to_strings (s : string) : string list =
    Str.split (Str.regexp "[ \t]+") s;;

(* Method from pleac.sourceforge.net *)
let is_Integer (s : string) : bool =
    try ignore (int_of_string s); true with Failure _ -> false;;

let str_to_fn (s : string) : (int -> int -> int) =
    match s with 
    | "+" -> (+)
    | "-" -> (-)
    | "*" -> ( * )
    | "/" -> (/)
    | _ -> raise (Invalid_argument "Not an operator");;

let apply_and_store (lst : 'a list) (fn : 'a -> 'a -> 'a) : 'a list =
    match lst with 
    | one :: two :: tl -> fn two one :: tl
    | _ -> raise (Invalid_argument "List too short");;

let prec (s : string) : int =
    match s with
    | "+" | "-" -> 1
    | "*" | "/" -> 2 
    | _ -> raise (Invalid_argument "Not in operator table");;

let infix_to_postfix (lst : string list) : string list =
    let rec 
        push (elem : string) (output : string list) (ops : string list) : (string list * string list) =
            match ops with
            | [] -> (output, elem :: ops)
            | hd :: tl ->   if prec elem <= prec hd 
                            then push elem (hd :: output) tl
                            else (output, elem :: ops)
    in
    let rec 
        aux (l : string list) (output : string list) (ops : string list) : string list =
            match l with 
            | [] -> ops @ output
            | hd :: tl ->   if is_Integer hd 
                            then aux tl (hd :: output) ops 
                            else let ret = push hd output ops in 
                                aux tl (fst ret) (snd ret)
    in
    aux lst [] [];;

let eval_postfix (lst : string list) : int =
    let rec aux (l : string list) (stack : int list) : int =
        match l with 
        | [] -> List.nth stack 0
        | hd :: tl ->   if is_Integer hd 
                        then aux tl ((int_of_string hd) :: stack)
                        else aux tl (apply_and_store stack (str_to_fn hd)) in
    aux (List.rev lst) [];;

let eval_infix (s : string) : int =
    eval_postfix (infix_to_postfix (explode_to_strings s));;