MIT Scheme has been installed on all Sun workstations (i.e., flat-rock, hendersonville, and marshall), and on the linux boxes in 004. You can invoke the Scheme interpreter by giving the command:
scheme
at the system prompt. To leave the Scheme interpreter
give the command:
(exit)
To load files of Scheme code, use the function load:
(load "assign2.scm")
The primary URL is http://www.swiss.ai.mit.edu/projects/scheme/. This site contains a manual as well as various implementations.
There were also a number of good on-line resourses:
When writing routines that operate on disjoint union types, we generally write conditionals, which check which kind of thing we've got, of the multiple kinds of things that count as members of the larger type. For example:
a list is:
* the empty list, OR
* a pair whose cdr is a list
Notice that the main structure of this definition is a disjoint
union; the set of empty lists and the set of pairs don't overlap,
because an empty list is never a pair and vice versa. They're disjoint
types, with no members in common. But the type list is defined by
combining them; a list is either the empty list or a pair whose cdr is
also list.
Note that the first case, empty lists, is not recursive. But the second case is defined recursively. Not just any old pair is a list--only those whose cdrs are lists. When we do normal recursion over lists, this disjoint union definition guides us--we usually have some kind of conditional as the body of our function, which checks which of these two kinds of list we've got; in the second case, we use recursion where there's recursion in the definition:
(define (list-sum lis)
(if (null? lis) ;; empty list OR other kind?
0 ;; empty list case
(+ (car lis) ;; other case, of a pair
(list-sum (cdr lis))) ;; whose cdr is a list
Likewise, when we evaluate simple infix expressions, we have a disjoint union:
an _infix_expression_ is:
* a literal number, or
* a binary expression of the form
(<infix expression> <operator> <infix expression> )
an <operator> is:
+, or
-
When we think about the s-expression data structures we use to
represent these expressions, we get a disjoint union. Let's call a
data structure representing an infix expression an infix-expr.
an infix-expr is
* a number
* a three-element list whose
1st element is an infix-expr
2nd element is an operator symbol (+ or -)
3rd element is an infix-expr
From this description we can write a Scheme predicate to tell if
something is an infix-expr:
(define (infix-expr? thing) ;; a thing is an infix-expr if
(or (number? thing) ;; it's a number OR if
(and (list? thing) ;; it's a list, and
(= (length thing) 3) ;; it has exactly 3 elements, and
(infix-expr? (car thing)) ;; its car is an infix-expr, and
(member (cadr thing) '(+ -)) ;; its cadr is + or -, and
(infix-expr? (caddr thing))))) ;; its caddr is an infix-expr
(In the above code, cadr is a built-in function that
returns the car of the cdr of its argument, i.e., (car (cdr thing)).
The function caddr is similarly equivalent to (car (cdr (cdr
thing))).)
Note that our predicate corresponds exactly to the English definition of an infix-expr data structure, representing an infix expression in the obvious way. Similarly, if we want to know the value of of an infix expression represented by an infix-expr data structure, we can say that:
the value of an infix expression is:
* if it's a number, just that number, OR
* (if it's a binary expression)
if the operator is plus:
the sum of
the value of the left subexpression
and the value of the right subexpression
otherwise
the difference of the value of the left subexpression
and the value of the right subexpression
Notice that the structure of this definition of the value of an infix expression is a conditional--there are two kinds of infix expressions, and the value of each kind is computed differently. Also notice that the definition of the value of an infix expression is recursive in the case where the value isn't just a number--the value of a binary expression is defined in terms of the value of its left subexpression and the value of its right subexpression. These two basic facts guide the writing of the infix-eval function:
(define (infix-eval infix-expr)
(if (number? infix-expr) ;; if it's just a number
infix-expr ;; the value is that number
;; otherwise it's a binary expression whose value is:
;; if the operator is plus,
;; the sum of the values of the subexpressions
;; otherwise,
;; the difference of the values of the subexpressions
(if (eq? (cadr infix-expr) '+)
(+ (infix-eval (car infix-expr))
(infix-eval (caddr infix-expr)))
(- (infix-eval (car infix-expr))
(infix-eval (caddr infix-expr))))))
Notice that in this case, too, the basic structure is a conditional to discriminate between the two basic types in our disjoint union. And within the branch for the recursive case, we have recursion for each part that is recursive in the definition--a binary infix-expr has two subexpressions that are also infix-expr's, so when we compute the value of a binary infix-expr, we must compute the values of those infix-exprs (the car and the caddr).
Consider the following definition of a number tree:
a number-tree is:
* a number, OR
* a two-element list whose
first element is a number-tree
second element is a number-tree
By this definition, the following are number-trees:
1 2 (1 (2 3)) (((2 4) 6) (8 (10 11))) the following are NOT number trees: (1 (2)) (1 . 2) foo (1 2 3) (1) ((1 . 2) . (3 . 4))Think about why these don't meet the definition.
This kind of tree is like our expression trees, but with no operator symbol.
Example uses: (number-tree->list 1) => (1) (number-tree->list '(1 2)) => (1 2) (number-tree->list '((1 2) (3 4))) => (1 2 3 4) (number-tree->list '((4 8) ((2 3) 1))) => (4 8 2 3 1)
Now consider the following prefix arithmetic expressions using the operators + and -:
1 22 (+ 1 3) (- (+ 3 6) (- 2 1))
Example uses: (infix->prefix '1) => 1 (infix->prefix 22) => 22 (infix->prefix '(1 + (3 - 2))) => (+ 1 (- 3 2))Recall that a read-eval-print loop uses the function read to read an s-expression, evaluates it, and prints the result, then loops to do it over again, ad infinitum. Here's a read-eval-print-loop function using our prefix-eval function:
(define (repl)
(display "REPL>") ;; print prompt to show we're ready for input
(let ((input (read))) ;; read input
(display (infix-eval input)) ;; evaluate it and write result
(newline) ;; write a newline character
;; to start another line
(repl))) ;; call self to do it again
This uses the standard Scheme functions read (which can read
any s-expression that the user types in textually, converting it to a
data structure) and display, which can write the textual
representation of any s-expression for the user to see. We can call
the repl function above, and it will repeatedly:
Test your function as shown in the example below. REPL> is what the
repl function writes, what follows that is what you type, and the
next line is the result it should write. To get it to look nice when
you quit out, we put a call to newline after the call to display that
displays ``quitting...'' and before the line that returns #t.
(Note that after your function returns when you quit, the Scheme
system will print the value it returns. That's normal--you're letting
control go back to your Scheme system's own read-eval-print-loop, and
it just prints the value returned from the call to repl.)
(repl) ;; start up your read-eval-print-loop by calling repl function REPL>1 1 REPL>(1 + 3) 4 REPL>((1 + 3) - 2) 2 REPL>halt quitting... \#t
Test your new repl function using your infix-eval and your prefix-eval:
> (repl2 infix-eval) ;; start up with infix evaluator REPL2>(1 + (3 - 2)) 2 REPL2>5 5 REPL2>(6 - (3 + 1)) 2 REPL2>halt quitting... \#t > (repl2 prefix-eval) ;; start up with prefix evaluator REPL2>(+ 1 (- 3 2)) 2 REPL2>5 5 REPL2>(- 6 (+ 3 1)) 2 REPL2>halt quitting... \#t