MITTutorialApril2009: ListExercise.fss

File ListExercise.fss, 8.0 KB (added by jmaessen, 7 months ago)

Exercise 3: List comprehensions (edited for clarity)

Line 
1component ListExercise
2export Executable
3import List.{...}
4
5(* The example graph in this exercise is cribbed from
6   "Fast Decision Procedures Based on Congruence Closure",
7   by G. Nelson and D. C. Oppen.  They give a *far* more efficient
8   algorithm for solving the same problem!
9
10The initial graph looks like this:
11
12  f
13  |
14  f
15  |
16  f    g
17  |   / \
18  f  g   |
19  | / \  |
20  f  g | |
21  | / \|/
22  a    b
23
24Initially we assert that the following nodes are equivalent:
25a = f(f(f(a)))
26a = f(f(f(f(f(a)))))
27a = g(a, b)
28
29We end up discovering that all the nodes except b are equivalent!
30*)
31
32(* EXERCISE 1: samePairs *)
33
34(* Fill in the function samePairs that checks that its two input lists
35   contain the same pairs of values (either list may contain
36   duplicates, and order doesn't matter).
37
38   You will find BIG AND, BIG OR, and IN operators useful here.
39   Also, note that AND: and OR: yield short-circuit evaluation.
40 *)
41samePairs(a: List[\ (Node,Node) \], b: List[\ (Node,Node) \]): Boolean =
42    fail("Ex 1, samePairs, not yet started; "
43         "the other exercises won't check without it!")
44
45(* EXERCISE 2: uniq *)
46
47(* Fill in the function uniq that is passed a list of Nodes, and removes elements of the
48   list that are equal to preceding elements.
49
50   There are many ways to do this; a particularly nice (parallel) one
51   makes use of the split() method on lists.  Another possibility is to use
52   |xs| and indexing to extract sublists: x[0], x[1:], and so forth. *)
53uniq(xs: List[\(Node,Node)\]) =
54   fail("Ex 2, uniq, not yet started.")
55
56(* EXERCISE 3: symmetricTransitiveClosure *)
57
58(* Fill in the function symmetricTransitiveClosure, that given a list
59   of pairs computes the symmetric transitive closure of that list.
60   The list is symmetrically closed if for each pair (x,y) the list
61   also contains (y,x).  The list is transitively closed if, when it
62   contains (x,y) and (y,z), it also contains (x,z) when x =/= z.  You
63   can decide whether to include reflexive pairs (x,x) as well; this
64   exercise is somewhat easier if you do so.
65
66   Note that you'll need to decide when to terminate.  You can use
67   samePairs to decide this, or if you're careful you can use list
68   equality xs=ys (which is cheaper) or a check for emptiness
69   (xs.isEmpty, cheaper still).
70
71   You may find this problem easiest if you symmetrically close a,
72   then compute the transitive closure of the result.  We've provided
73   an appropriate type signature to help you along.  *)
74symmetricTransitiveClosure(a: List[\ (Node,Node) \]): List[\ (Node,Node) \] =
75    fail("Ex 3, symmetricTransitiveClosure, not yet started.")
76
77(* Assumption: a is symmetrically closed. *)
78transitiveClosure(a: List[\ (Node,Node) \]): List[\ (Node,Node) \] =
79    fail("transitiveClosure is used but not implemented.")
80
81(* EXERCISE 4: equivalence by operator *)
82
83(* a list of Nodes, find the symmetric, transitively
84   closed list of Nodes with the same operator.  The operator of a
85   node n can be obtained by writing n.op (so you'll want to write
86   code of the form n.op = m.op)
87*)
88operatorClosure(nodes: List[\Node\]): List[\(Node,Node)\] =
89    fail("Ex 4, operatorClosure, not yet started.")
90
91(* EXERCISE 5: graph equivalence closure *)
92
93(* Given a graph, we define its *equivalence closure*, a
94   symmetric, transitively closed list for which the following are true:
95       If (a,b) is in initEquivs, it is in the equivalence closure.
96       If two nodes a and b have the same operator (they're in the operatorClosure),
97           and corresponding pairs of children are equal or in the equivalence closure,
98           then (a,b) are also in the equivalence closure.
99   For this exercise you'll want to use transitiveClosure, operatorClosure,
100       and zip.
101      xs.zip[\T\](ys) generates corresponding pairs of elements from xs and ys; for example
102        <| x | (x,y) <- <|1,2,3|>.zip[\ZZ32\]<|1,4,3,2|>, x=y |>
103      generates the list of elements <1,3>.
104   To obtain a list of the children of node n, simply write n.children.
105 *)
106equivalenceClosure(graph: List[\Node\], initEquivs: List[\(Node,Node)\]): List[\(Node,Node)\] =
107    fail("Ex 5, equivalenceClosure, not yet started.")
108
109(************************************************************
110 * Built-in utility code down here
111 ************************************************************)
112
113value object Node(name: String, op: String, children: List[\Node\]) extends Equality[\Node\]
114    getter asString(): String = name
115    opr =(self, other:Node): Boolean = name = other.name
116end
117
118node(op: String, children: List[\Node\]): Node = do
119    name = op "(" (BIG ||[c <- children] (", " c.name))[2:] ")"
120    Node(name, op, children)
121  end
122
123node(op: String, childNodes: Node...): Node =
124    node(op, list[\Node\](childNodes))
125
126leaf(name: String): Node = Node(name, name, <|[\Node\] |>)
127
128showEquiv(eqvs: List[\(Node,Node)\]): String =
129    BIG //[(x,y) <- eqvs, |x.name| < |y.name| OR: x.name < y.name] x.name " = " y.name
130
131(************************************************************
132 * TESTS
133 ************************************************************)
134
135run(): () = do
136    a = leaf("a")
137    fa = node("f",a)
138    ffa = node("f",fa)
139    fffa = node("f",ffa)
140    ffffa = node("f",fffa)
141    fffffa = node("f",ffffa)
142    b = leaf("b")
143    gab = node("g",a,b)
144    gfab = node("g",fa,b)
145    ggfab = node("g",gfab,b)
146    graph = <|[\Node\] a,fa,ffa,fffa,ffffa,fffffa,b,gab,gfab,ggfab |>
147    initEquivs = <|[\(Node,Node)\] (a,fffa), (a,fffffa), (a,gab)|>
148    iE = <|[\(Node,Node)\] (a,gab), (a,fffa), (a,fffffa) |>
149    eE = <|[\(Node,Node)\] |>
150    (* EXERCISE 1: samePairs *)
151    assert(samePairs(iE, iE), true, "Ex 1: Should be samePairs",iE,iE)
152    assert(samePairs(initEquivs, iE), true, "Ex 1: Should be samePairs",initEquivs,iE)
153    assert(samePairs(eE, eE), true, "Ex 1: Should be samePairs",eE,eE)
154    iE2 = initEquivs || iE
155    assert(samePairs(iE, iE2), true, "Ex 1: Should be samePairs",iE,iE2)
156    assert(samePairs(eE, iE), false, "Ex 1: Not samePairs", eE, iE)
157    iEr = iE[1:]
158    assert(samePairs(iE2, iEr), false, "Ex 1: Not samePairs", iE2, iEr)
159    println("EXERCISE 1 Complete")
160    (* EXERCISE 2: uniq *)
161    assert(initEquivs, uniq(initEquivs), "Ex 2: Uniq is different")
162    assert(initEquivs, uniq(initEquivs || iE), "Ex 2: Uniq is different")
163    assert(eE, uniq(eE), "Ex 2: Uniq should be empty!")
164    println("EXERCISE 2 Complete")
165    (* EXERCISE 3: symmetricTransitiveClosure *)
166    initEquivs_closed = symmetricTransitiveClosure(initEquivs)
167    reflexives = <|[\(Node,Node)\] (a,a), (fffa, fffa), (fffffa, fffffa), (gab, gab) |>
168    expected_closure =
169        ( initEquivs || reflexives ||
170          <|[\(Node,Node)\] (fffa, a), (fffa,fffffa), (fffa, gab),
171                        (fffffa, fffa), (fffffa, a), (fffffa, gab),
172                        (gab, fffa), (gab, fffffa), (gab, a) |> )
173    assert(samePairs(initEquivs_closed || reflexives, expected_closure), true,
174           "Ex 3: Closure doesn't have same pairs as expected\n",
175           initEquivs_closed, "\n", expected_closure)
176    println("EXERCISE 3 Complete")
177    (* EXERCISE 4: closure by op *)
178    op_closure = operatorClosure(graph)
179    reflexive_graph = <|[\(Node,Node)\] (n,n) | n <- graph |>
180    expected_op_closure =
181        symmetricTransitiveClosure( reflexive_graph ||
182          <|[\(Node,Node)\]
183            (fa,ffa),(fa,fffa),(fa,ffffa),(fa,fffffa),
184            (gab,gfab),(gab,ggfab) |> )
185    assert(samePairs(op_closure || reflexive_graph, expected_op_closure), true,
186           "Ex 4: Closure by operator doesn't have same pairs as expected\n",
187           op_closure, "\n", expected_op_closure)
188    println("EXERCISE 4 Complete")
189    (* EXERCISE 5: equivalence closure *)
190    fullClose = <| (n1,n2) | n1 <- graph, n1.name =/= "b", n2 <- graph, n2.name =/= "b" |>
191    equiv_closure = equivalenceClosure(graph, initEquivs)
192    println("Initial equivalences:" // showEquiv(initEquivs))
193    println("\nFinal equivalences:" // showEquiv(equiv_closure))
194    assert(samePairs(fullClose||reflexive_graph, equiv_closure||reflexive_graph), true,
195           "Ex 5: Closure by operator is missing some pairs!\n",
196           fullClose, "\n", equiv_closure)
197    println("EXERCISE 5 Complete")
198  end
199
200end