; VERSION for UMB-SCHEME (uses only argument for call to "eval"). ;(load "blocks.scm") ; uncomment this if you run this file standalone... ; GRAPHROW graphs a row of blocks, where "row" is a list of blocks, with ; null (empty list) indicating no block in a particular column. For example, ; if row = (e b g p () q), that would give six columns (we use a space between ; each column), and GRAPHROW would print out " e b g p q". (define (graphrow row) (if (empty? row) '() (begin (if (null? (car row)) (display #\space) (display (car row)) ) (display #\space) (graphrow (cdr row)) ) ) ) ; GRAPHALLROWS is given a matrix, which is a list of rows of blocks to print, in ; the order they are to be printed. It simply calls GRAPHROW on each row to be ; printed, in order, and also pretties up the output by indenting all rows by two ; spaces, printing a carriage return (newline) after each row is graphed, and then ; after all rows are graphed it prints a line of dashes below the stacks of blocks ; (i.e., one dash for each column) to represent the table. (define (graphallrows matrix) (if (null? matrix) (begin (display #\space)(display #\space)(display #\space) (display '-)(display '-)(display '-)(display '-)(display '-)(newline)) (begin (display #\space)(display #\space)(display #\space) (graphrow (car matrix)) (newline) (graphallrows (cdr matrix)) ) ) ) ; NOBLOCKSLEFT is a boolean function that is given a state s, where s is a list of rows, ; (or a list of columns; it will work in both cases), and checks to see if there are ; any blocks present in any row/column. It does this by simple looking to each sublist ; (which is a row or column) in s, and counting the number of elements (blocks) in each, ; adding these row/column counts up, and checking to see if the sum is zero or not. ; Thus s = ( (a e f) (d e) (c b) ) would have a count of 7 and so NOBLOCKSLEFT would ; return #f, while s = ( () () () ) would have a count of 0 and so NOBLOCKSLEFT would ; return #t. (define (noblocksleft? s) (equal? 0 (eval (cons '+ (map length s)) )) ) ; FIXEDCDR "fixes" the behavior of SCHEME's "cdr" to it behaves like Lisp's "cdr", ; in the sense that "(cdr '())" should return '(), which it does in Lisp, but in SCHEME ; it gives an error. FIXEDCDR returns '() for "(fixedcdr '())". For all other inputs, ; that is "(fixedcdr L)" where L is a non-empty list, FIXEDCDR returns the regular "cdr" ; of L. (define (fixedcdr L) (if (null? L) '() (cdr L)) ) ; FIXEDCAR "fixes" the behavior of SCHEME's "car" to it behaves like Lisp's "car", ; in the sense that "(car '())" should return '(), which it does in Lisp, but in SCHEME ; it gives an error. FIXEDCAR returns '() for "(fixedcar '())". For all other inputs, ; that is "(fixedcar L)" where L is a non-empty list, FIXEDCAR returns the regular "car" ; of L. (define (fixedcar L) (if (null? L) '() (car L)) ) ; GETROW is one of the functions used to turn a state s, which is a list of columns of ; blocks, into a list of rows of blocks, which is needed for GRAPHALLROWS to display. ; GETROW takes as input a reversed state rs, which is simply rs = (apply reverse s) ; which is some state s but with the order of blocks in each column reversed, so that ; the blocks in each column are listed in bottom to top order, rather than the top to ; bottom order in a normal state s. GETROW takes this "reversed order" state and gets ; the first row by taking the first element from each inverted stack/column. It does ; this by car'ing each element of rs. Note how it used FIXEDCAR instead of SCHEME's ; car. This is so that an empty column (i.e., '()) gets an entry of '() for the block ; in that column in the row being constructed and returned. (define (getrow rs) (if (null? rs) '() (cons (fixedcar (car rs)) (getrow (cdr rs)) ) ) ) ; GETALLROWS simply applies GETROW to the given rs (see comments for GETROW for a ; a description of the "reversed order" state rs), so that what is returned is a ; list of the rows to be printed, from bottommost to topmost row. Thus GETALLROWS ; takes a list of columns (but in which each block stack is in reverse order -- thus ; in bottom to top order) and produces a list of rows to be printed, also in bottom to ; top order. But note that this bottom-to-top ordering of row is the opposite order ; from what GRAPHALLROWS wants, since it needs to print rows top to bottom down the ; screen. So the calling function, GRAPH, reverses the output of this function, ; GETALLROWS, before giving it to GRAPHALLROWS. (define (getallrows rs) (if (noblocksleft? rs) '() (cons (getrow rs) (getallrows (map fixedcdr rs))) ) ) ; GRAPH takes a state, in normal "state format" which is a list of columns, with ; each column a list of blocks in top to bottom order, with '() representing an ; empty stack/column. GRAPH then reverses the order of blocks in each column so ; that it can then call GETALLROWS to format the columns into printable rows, ; then GRAPH reverse the order of the printable rows so that they are top-to-bottom ; before finally calling GRAPHALLROWS to print each row, to to bottom. GRAPH ; also prints two carriage returns before printing the block stacks. GRAPH ; returns '(). (define (graph state) (begin (newline)(newline) (graphallrows (reverse (getallrows (map reverse state)))) ) ) (define (last L) (car (reverse L))) (define (graphplan plan) (if (not (equal? (car plan) 'quote)) (graphplan (last plan)) ) (graph (eval plan)) )