about summary refs log tree commit diff stats
path: root/html/archive/2.vm/edit.png
Commit message (Expand)AuthorAgeFilesLines
* 5485 - promote SubX to top-levelKartik Agaram2019-07-271-0/+0
'#n11'>11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
CS 61A			Week 15 Solutions

LAB
===

4.55

(supervisor ?x (Bitdiddle Ben))

(job ?x (accounting . ?y))

(address ?x (Slumerville . ?y))

The dots are needed because (accounting ?y), for example, would match
only entries in which there was a single element after the word "accounting."
That is, (accounting ?y) would match (accounting scrivener) but not
(accounting chief accountant).


4.62
The base case here involves a 1-element list, not the empty list.

(rule (last-pair (?x) (?x)))

(rule (last-pair (?y . ?z) ?x)
      (last-pair ?z ?x))


HOMEWORK
========

4.56

(and (supervisor ?x (Bitdiddle Ben))
     (address ?x ?y))

(and (salary ?x ?s1)
     (salary (Bitdiddle Ben) ?s2)
     (lisp-value < ?s1 ?s2))

(and (supervisor ?who ?boss)
     (not (job ?boss (computer . ?y)))
     (job ?boss ?z))

The key point here is that we use the same variable name twice if we want
it to match the same item both times.


4.57

(rule (same ?x ?x))		;; Don't use (lisp-value eq? ....)

(rule (replace ?p1 ?p2)
      (and (or (and (job ?p1 ?x) (job ?p2 ?x))
	       (and (job ?p1 ?x) (job ?p2 ?y) (can-do-job ?x ?y)))
      	   (not (same ?p1 ?p2))))

(replace ?x (Fect Cy D))

(and (replace ?x ?y)
     (salary ?x ?s1)
     (salary ?y ?s2)
     (lisp-value < ?s1 ?s2))


4.58
Note the definition of a sub-rule to make things more manageable.

(rule (sup-in-div ?p ?x)
      (and (supervisor ?p ?boss)
	   (job ?boss (?x . ?z))))

(rule (big-shot ?person ?division)
      (and (job ?person (?division . ?x))
	   (not (sup-in-div ?person ?division))))


4.65
This problem requires understanding the basic idea of how the
query system works (read Section 4.4.3).
To respond to a query, the query system generates
a stream of frames which are then used to "instantiate" the query.
In this case, the stream will include frames containing all bindings of
?middle-manager, ?person and ?x satisfying the body of the rule,
and also with ?who bound to ?person.
Since Warbucks supervises Bitdiddle and Scrooge, each of who manages
other people, there will be more than one of these frames.
Hence Warbucks appears more than once in the output.


Extra for Experts
=================

Here's the REVERSE from lecture:

    (assert! (rule (reverse (?a . ?x) ?y)
		   (and (reverse ?x ?z)
			(append ?z (?a) ?y) )))

    (assert! (reverse () ()))

Why won't this run backwards?  It's important to understand this, in order to
solve the problem.  Unfortunately there are a lot of details, so here's a
preview of the punch line:  It'll turn out that the query system tries to use
the recursive rule over and over, in effect constructing longer and longer
lists whose elements aren't known, and never realizing that they can't
possibly be the reverse of the given list.

Let's try to work out what happens if we give the simplest possible
backwards query:

    (reverse ?b (3))

The answer we want is (reverse (3) (3)).  QEVAL starts with the stream of
frames containing one empty frame:

    {[]}

it matches the query against everything in the database.  Only two are
relevant -- the ones about REVERSE.  Starting with the base case assertion

    (reverse () ())

we see that this doesn't match the query, because (3) in the third element of
the query is not () and neither of them is a variable.  That leaves the
recursive rule.  We unify the query against the conclusion of the rule,
after renaming the variables in the rule:

    (reverse ?b (3))
    (reverse (?1a . ?1x) ?1y)

This succeeds, and the empty frame is extended with new bindings:

    [?b = (?1a . ?1x), ?1y = (3)]

Now we use this frame as the starting point for a new query, the rule's body:

    (and (reverse ?1x ?1z) (append ?1z (?1a) ?1y))

Now it gets a little complicated.  QEVAL of an AND query starts by
evaluating the first part in the current frame.  We match

    (reverse ?1x ?1z)

against all rules and assertions.  Again, let's start with the base case,
so we are matching

    (reverse ?1x ?1z)
    (reverse () ())

This extends the frame with new bindings for ?1X and ?1Z:

    [?b = (?1a . ?1x), ?1y = (3), ?1x = (), ?1z = ()]

With these bindings we have to evaluate the second part of the AND:

    (append ?1z (?1a) ?1y)

Substituting values from the frame, this is equivalent to

    (append () (?1a) (3))

which will work fine (leaving out the details about APPEND), giving a
final extended frame of

    [?b = (?1a . ?1x), ?1y = (3), ?1x = (), ?1z = (), ?1a = 3]

So ?b = (?1a . ?1x) = (3 . ()) = (3).

This is a fine solution, and if the query system looks at assertions
before rules, it may even be printed before the evaluator gets into an
infinite loop.  The problem is with the recursive REVERSE rule.

Remember that we are trying to evaluate the query

    (and (reverse ?1x ?1z) (append ?1z (?1a) ?1y))

and that the first step is to evaluate

    (reverse ?1x ?1z)

in the frame

    [?b = (?1a . ?1x), ?1y = (3)]

We've matched the query against the base case for REVERSE, and now we are
trying the recursive rule.  Here are the query and the conclusion (with
variables again renamed) of the rule:

    (reverse ?1x ?1z)
    (reverse (?2a . ?2x) ?2y)

This succeeds; the resulting frame is

    [?b = (?1a . ?1x), ?1y = (3), ?1x = (?2a . ?2x), ?1z = ?2y]

In this frame we must evaluate the body of the rule, namely

    (and (reverse ?2x ?2z) (append ?2z (?2a) ?2y))

Match the REVERSE part against the conclusion of the REVERSE rule
with variables renamed:

    (reverse ?2x ?2z)
    (reverse (?3a . ?3x) ?3y)

This extends the frame some more:

    [?b = (?1a . ?1x), ?1y = (3), ?1x = (?2a . ?2x), ?1z = ?2y,
     ?2x = (?3a . ?3x), ?2z = ?3y]

We human beings can see that this is all nonsense.  Combining some of the
bindings we see that

    ?b = (?1a . (?2a . (?3a . ?3x)))

which is a list of at least three elements.  So if we ever got to the
APPEND part of the rule, it wouldn't match -- the result of reversing (3)
can't be more than one element long!  But QEVAL will never get around to
the second half of the AND query, because it keeps finding longer and
longer lists to try to reverse.

Why isn't this a problem when running the REVERSE rules forward?  Let's
take the query

    (reverse (35) ?d)

This doesn't match the base case, so we try the recursive case renamed:

    (reverse (35) ?d)
    (reverse (?4a . ?4x) ?4y)

We can see a difference right away:  It's the known list, (35), that we
divide into its car and its cdr, giving determined values for some of
the variables in the new frame:

    [?4a = 35, ?4x = (), ?d = ?4y]

We must now evaluate the body of the rule:

    (and (reverse ?4x ?4z) (append ?4z (?4a) ?4y))

I'll skip the part about matching the new REVERSE query against the base
case, which again gives a correct result.  Instead let's see what happens
when we try to use the recursive rule again:

    (reverse ?4x ?4z)
    (reverse (?5a . ?5x) ?5y)

This unification fails!  We want ?4x = (?5a . ?5x), but the frame tells us
that ?4x is empty.

This is why forward reverse doesn't get into an infinite loop: QEVAL notices
that the recursive rule can't apply when we get past the number of elements
in the original list.

----------

That's the end of the analysis of what's wrong.  The recursive rule is
supposed to say "the reverse of my known length-N list (?a . ?x) can be
computed if we first take the reverse of a list of length N-1, namely ?x."
But when run backwards it instead says "the reverse of my known list ?y
consists of a (first) element ?1a followed by a list consisting of an
element ?2a followed by a list consisting of an element ?3a followed ..."

We don't have this problem running the rules forwards because the rule
takes our known list and divides it into car and cdr, so we find out as
soon as we run out of list elements.  The algorithm doesn't require us
to divide the second list, ?y, into pieces, and the cdr of ?y isn't useful
in doing the recursion -- we need all of ?y.  So we'll add an extra
variable whose only purpose is to count down the length of ?y:

(assert! (rule (reverse ?x ?y)
	       (reverse-help ?x ?y ?y)))

(assert! (rule (reverse-help (?a . ?x) ?y (?ignore . ?counter))
	       (and (reverse-help ?x ?z ?counter)
		    (append ?z (?a) ?y))))

(assert! (rule (reverse-help () () ())))

On each recursive invocation of the REVERSE-HELP rule, ?COUNTER gets
smaller.  When it's empty, no more recursions are possible, because an
empty list can't match (?ignore . ?counter).

For forwards queries, the whole counter mechanism is unhelpful, but it
doesn't hurt.  It's the (?a . ?x) that prevents infinite recursion for
forwards queries; the ?counter situation is just like the ?x situation
we saw before for backwards queries -- in effect we get

    ?1counter = (?2ignore . (?3ignore . (?4ignore . ?4counter)))

after three invocations of the rule.  That could keep going on forever,
but the values of ?1x, ?2x, etc., are *known* and therefore eventually
one of them is empty and won't match the recursive rule.

----------

This solution, like the partial solution in the lecture notes, is based on
the recursive-process Scheme procedure

    (define (reverse seq)
      (if (null? seq)
	  '()
	  (append (reverse (cdr seq)) (list (car seq)))))

What if we start instead with the iterative-process version:

    (define (reverse seq)
      (define (iter seq result)
	(if (null? seq)
	    result
	    (iter (cdr seq) (cons (car seq) result)))))

We still have to add an extra counter variable to make this work as a
both-ways logic program, in addition to the Scheme program's extra
result variable:

    (assert! (rule (reverse ?x ?y)
		   (reverse-iter ?x () ?y ?y)))

    (assert! (rule (reverse-iter (?a . ?x) ?result ?y (?b . ?counter))
		   (reverse-iter ?x (?a . ?result) ?y ?counter)))

    (assert! (rule (reverse-iter () ?y ?y ())))