The pattern matching of ML would make this a piece of cake.
I will use SML/NJ for my example.
datatype expr = Const of real | Var of string | Plus of expr*expr | Minus of expr*expr | Times of expr*expr | Div of expr*expr | Collection of expr list; fun eval1(e:expr) : expr = case e of Div(Times(a,b),c) => Times(a, Times(b, Div(Const(1.0), c))) |_ => efun eval2(e:expr) : expr = case e of Div(Times(a,b),c) => Times(Div(a, c), Div(b,c)) |_ => efun eval3(e:expr) : expr = case e of Div(Plus(a,b),c) => Plus(Div(a, b), Div(b,c)) |_ => e fun eval4(e:expr) : expr = case e of Div(Times(a,b),Const(n)) => Times(a, Times(b, Const(1.0/n))) |_ => efun eval5(e:expr) : expr = let fun evalHelper(l: expr list, b:expr) : expr = case l of h::[] => Div(h,b) |h::t => Times(Div(h,b), evalHelper(t, b)) |_ => raise Fail "Should never occur" in case e of Div(Collection(a), b) => evalHelper(a,b) |_ => e end
I think that should work. Those are some pretty specific rules. You can add a more general eval function that will actually parse it out and evaluate it for you. Something like:
fun eval(e:expr) : real = case e of Const(a) => a |Plus(a,b) => eval(a) + eval(b) |Minus(a,b) => eval(a) - eval(b) |Div(a,b) => eval(a) / eval(b) |Times(a,b) => eval(a) * eval(b) |_ => raise Fail "Cannot parse type."
To do variables, you could have assocation list such as:
type association = string*realfun lookup(a:string, l:association list) : real = case l of [] => raise Fail "not in list" |(x,y)::t => if a = x then y else lookup(a,t)
Then, in you eval function, just change the code to pass in an association list and when it is of type Variable(a), simply put "=> lookup(a, l)", where l is the association list.
Anyway, I hope that is what you were looking for. Your rules were rather specific -- but it can definitely easily be done using SML.
Here is some usage of the code from the SML interpreter
- Div(Times(Const(2.0), Const(4.0)), Const(12.0));val it = Div (Times (Const #,Const #),Const 12.0) : expr- eval1(it);val it = Times (Const 2.0,Times (Const #,Div #)) : expr- eval(it);val it = 0.666666666667 : real
With some extra "to string" code...
fun toString(e:expr) : string = case e of Const(a) => Real.toString(a) |Plus(a,b) => toString(a) ^ "+" ^ toString(b) |Minus(a,b) => toString(a) ^ "-" ^ toString(b) |Div(a,b) => toString(a) ^ "/" ^ toString(b) |Times(a,b) => toString(a) ^ "*" ^ toString(b)
With interpreter...
- Div(Times(Const(2.0), Const(4.0)), Const(12.0));val it = Div (Times (Const #,Const #),Const 12.0) : expr- eval1(it);val it = Times (Const 2.0,Times (Const #,Div #)) : expr- toString(it);val it = "2.0*4.0*1.0/12.0" : string
[Edited by - visage on September 21, 2006 5:01:55 PM]