(* Carsten Schuermann *) (* A SQL interpreter *) (* Fri Feb 9 23:12:11 2007 *) datatype Data = Int of int | String of string | Real of real | Pair of Data * Data type Table = Data list (* Problem 1: Define the filter function : Table -> (Data -> Bool) -> Table *) fun filter (T:Table) P = foldr (fn (x, l) => if P x then x :: l else l) nil T (* Problem 2: Define the join function : Table -> Table -> Table *) fun join T1 T2 = foldr (fn (x, t1) => t1 @ (foldr (fn (y, t2) => (Pair (x, y)) :: t2) nil T2)) nil T1 type layout = string -> Data type layoutInverse = Data -> string fun load (s, l : layout) = let val f = TextIO.openIn s fun load' () = case (TextIO.inputLine f) of SOME s => l s :: load' () | NONE => nil val result = load' () val _ = TextIO.closeIn f in result end fun save T (s, li : layoutInverse)= let val f = TextIO.openOut s fun save' nil = () | save' (H :: T) = (TextIO.output (f, li H); save' T) val result = save' T val _ = TextIO.closeOut f in () end datatype Stack = Null | Cons of Stack * Table datatype Exp = From of Table * Exp | Dup of Exp | Join of Exp | Where of (Data -> bool) * Exp | Iterate of (Data * Table -> Table) * Exp | Select of (Data -> Data) * Exp | Load of string * layout * Exp | Save of string * layoutInverse * Exp | End fun run S (From (T, E)) = run (Cons (S, T)) E | run (Cons (S, T)) (Dup E) = run (Cons (Cons (S, T), T)) E | run (Cons (Cons (S, T1), T2)) (Join E) = run (Cons (S, (join T1 T2))) E | run (Cons (S, T)) (Where (P, E)) = run (Cons (S, (filter T P))) E | run (Cons (S, T)) (Iterate (F, E)) = run (Cons (S, foldr F nil T)) E | run (Cons (S, T)) (Select (F, E)) = run (Cons (S, map F T)) E | run S (Load (s, f, E)) = run (Cons (S, load (s, f))) E | run (Cons (S, T)) (Save (s, f, E)) = (save T (s, f); run (Cons (S, T)) E) | run (Cons (S, T)) End = T (* Table 1: A table full of numbers *) val T0 : Table = map (fn x => Int x) [1,2,3,4,5,6,7,8,9] (* Example 1: Compute the sum of all numbers in T0 *) val E1 : Table = run Null (From (T0, Iterate (fn (Int x, []) => [Int x] | (Int x, [Int r]) => ([Int (x+r)]), End))) (* Example 2: Compute the sum of all even numbers in T0 *) val E2 = run Null (From (T0, Where (fn (Int x) => x mod 2 = 0, Iterate (fn (Int x, []) => [Int x] | (Int x, [Int r]) => [Int (x + r)], End)))) (* Example 3: Compute the list of all squares that are odd *) val E3 = run Null (From (T0, Iterate (fn (Int x, r) => if x * x mod 2 = 0 then r else Int x :: r, End))) (* Example 4: Compute the entries of a multiplication table *) val E4 = run Null (From (T0, Dup ( Join ( Iterate (fn (Pair (Int x, Int y), r) => Pair (Pair (Int x, Int y), Int (x * y)) :: r, End))))) (* Example 5: Compute the entries of a multiplication table and save it in the file "mult-table" *) val E5 = run Null (From (T0, Dup ( Join ( Iterate (fn (Pair (Int x, Int y), r) => Pair (Pair (Int x, Int y), Int (x * y)) :: r, Save ("mult-table", fn (Pair (Pair (Int x, Int y), Int z)) => Int.toString x ^ " * " ^ Int.toString y ^ " = " ^ Int.toString z ^ "\n", End)))))) (* Table 2: Contains parent relationships. (Child, (Father, Mother)) *) val Parents : Table = [Pair (String "Mary", Pair (String "George", String "Alice")), Pair (String "Bob", Pair (String "George", String "Alice")), Pair (String "George", Pair (String "Robert", String "Hillary")), Pair (String "Alice", Pair (String "Max", String "Lena")), Pair (String "Peter", Pair (String "Max", String "Lena")), Pair (String "Lena", Pair (String "Rudolph", String "Magret"))] (* Example 6: Compute the grandparent relationship from the parent relationship Table of records of the form (c, ((fm, ff), (mf, mm)) *) val E6 = run Null (From (Parents, Dup ( Dup ( Join ( Join ( Where (fn (Pair (Pair (String c1, Pair (String f1, String m1)), Pair (Pair (String c2, Pair (String f2, String m2)), Pair (String c3, Pair (String f3, String m3))))) => f1 = c2 andalso m1 = c3, Select (fn (Pair (Pair (c1, Pair (String f1, String m1)), Pair (Pair (String c2, Pair (f2, m2)), Pair (String c3, Pair (f3, m3))))) => Pair (c1, Pair (Pair (f2, m2), Pair (f3, m3))), Save ("grandparents", fn (Pair (String c1, Pair (Pair (String f2, String m2), Pair (String f3, String m3)))) => c1 ^ " grandparents are " ^ f2 ^ ", " ^ m2 ^ ", " ^ f3 ^ ", " ^ m3 ^ ".\n", End))))))))) (* Layouts: Layouts are functions that convert strings (read from a file) into meaning full data*) val LayoutExchangeRate : layout = fn s => Pair (String (String.substring (s, 0, 10)), Real (valOf (Real.fromString (String.substring (s, 20, 10))))) val LayoutExchangeRateInverse : layoutInverse = fn (Pair (String s, Real r)) => s ^ " " ^ String.substring (Real.toString (r) ^ "0000000000", 0, 10) ^ "\n"; (* Example 7: Read two files which contain the exchange rate between dkk and dollars and dollars with euros. Create a new table which contains the exchange rate between dkk and euros. *) val E7 = run Null (Load ("dkk-dollar", LayoutExchangeRate, Load ("dollar-euro", LayoutExchangeRate, Join ( Where (fn (Pair (Pair (String d1, _), Pair (String d2, _))) => d1 = d2, Select (fn (Pair (Pair (d1, Real r1), Pair (_, Real r2))) => Pair (d1, Real (r1 * r2)), Save ("my-result", LayoutExchangeRateInverse, End))))))) (* Example 8: Do the same thing as above, but this time, in such a way that we also take into account the exchange rate between euro and dkk *) val E7 = run Null (Load ("dkk-dollar", LayoutExchangeRate, Load ("dollar-euro", LayoutExchangeRate, Join ( Where (fn (Pair (Pair (String d1, _), Pair (String d2, _))) => d1 = d2, Select (fn (Pair (Pair (d1, Real r1), Pair (_, Real r2))) => Pair (d1, Real (r1 * r2)), Load ("euro-dkk", LayoutExchangeRate, Join ( Where (fn (Pair (Pair (String d1, _), Pair (String d2, _))) => d1 = d2, Select (fn (Pair (Pair (d1, Real r1), Pair (_, Real r2))) => Pair (d1, Real (r1 * r2)), Save ("exchangerate-study", LayoutExchangeRateInverse, End))))))))))) (* Table : Chemical elements *) val T3 = [Pair (Pair (String "H", Int 1), Real ~259.14), Pair (Pair (String "He", Int 2), Real ~272.00), Pair (Pair (String "Li", Int 3), Real 180.54), Pair (Pair (String "Be", Int 4), Real 1278.00), Pair (Pair (String "O", Int 8), Real ~218.36)]