Rice University

COMP 210: Principles of Computing and Programming

Lecture #9       Fall 2003

Finish the examples in the last lecture....

Note that there is an in-class exam coming up on 9/24. See the Exam Resources page for additional review materials.


The Law of Scheme

In a nutshell, here's everything Scheme does: It translates expressions into values, as follows:

 
An expression is... Examples How to evaluate
a value, 17, true, 'arugula You have the value already.
a placeholder, pi, + Get its defined value.
a function application, (+ (* 9 4) pi), (fahr->celc -40) Evaluate the subexpressions (exp0 exp1 ... expn) to get the values (val0 val1 ... valn).
  • If val0 is built-in function (e.g. +), do the appropriate built-in computation, resulting in the desired value.
  • If val0 is user-defined, rewrite the function's body, replacing the parameters with the actual arguments val1 ... valn. Evaluate the resulting expression.
or, a Special Form define, define-struct, cond; and, or various, but not shocking

One should note that a value represented by the characters "17" is in essence a placeholder for the internal representation of seventeen. Thus when evaluating such a value, Scheme actually does some work in looking up the internal representation and then using it. For things like numbers and booleans, the internal representation is quite different than that used to represent them on the screen.

Note anything familiar? The rules of evaluation are recursive. If you wanted, you could make your own data definitions to represent (your versions of) numbers, placeholders, function-call, and define; then you could use the design recipe, and write a scheme evaluator -- the core of drscheme -- yourself! (After seeing mutually-recursive data structures next week, this is a reasonable week-4 scheme homework (book exercises 14.4.1 -- 4 and 17.7.1 -- 4). Elegance speaks for itself!

Note: Over the rest of the semester, we will learn (just) three more special forms. (The formal definition of the corresponding part of C++ or Java requires about dense 30 pages, instead of 1. Those specifications have also been found to contain inconsistencies and errors, which have taken a long time to get revised.)

Digression: "The Proper Use of Quotation Marks" by Ian Barland.

 

Hand Evaluation:

Click here for a demonstration of hand evaluation in Scheme.

 

Lists of Mixed Data

Caveat: Don't believe anything said for the rest of this lecture. Listen and question but in the end, make up your own mind.

Just a slight twist on the same old topic: what if a list contains not just symbols, but symbols AND numbers mixed together?

Lists can contain more than one kind of data. Consider as a simple example a list containing both numbers and symbols. The data definition for such a list is as follows:

 

Data definition:
A list-of-nums-and-syms is one of
 - empty
 - (cons symbol list-of-nums-and-syms)
 - (cons num list-of-nums-and-syms)

Data definition:
A Recipe is a list-of-nums-and-syms.
We could use such a list to represent a recipe. Each symbol names an ingredient and each number indicates how long to cook before adding the next ingredient.

Note: Is this the most appropriate definition for a recipe? What are some alternatives?
One possibility:

A stage is: A list of ingredients and a cooking time.
A recipe is: a list of stages.
This is fine -- you'd have a template for list-of-stages, for stages, and for list-of-ingredients. But we'll stick with the list-of-nums-and-syms for now. Your representation should reflect how you think about the problem, but different people might think differently!

One other note about the details of list-of-nums-and-syms: It suffers the drawback that it doesn't separate the processing of the list, from the processing of an individual items. See Lab 3 for a another approach.

; Examples of list-of-nums-and-syms: 
;
empty
(define raw-mallow   (cons 'marshmallow empty))
(define roast-mallow (cons 'marshmallow (cons 1 empty)))
(define stir-fry
  (cons 'onions (cons 'garlic (cons 2 (cons 'tofu (cons 8 (cons 'spinach (cons 1 empty))))))))
What does the template for a program over list-of-nums-and-syms look like?

(define (fun a-lons ..)
  (cond [(empty? a-lons) ..]
        [(symbol? (first a-lons)) ..(first a-lons)..
                                  ..(fun (rest a-lons)).. ]
        [(number? (first a-lons)) ..(first a-lons)..
                                  ..(fun (rest a-lons)).. ]))
Or, perhaps a little more robustly:


(define (fun a-lons ..)
  (cond [(empty? a-lons) ..]
        [(cons? a-lons)  
            (cond	
                [(symbol? (first a-lons)) ..(first a-lons)..
                                          ..(fun (rest a-lons)).. ]
                [(number? (first a-lons)) ..(first a-lons)..
                                          ..(fun (rest a-lons)).. ])]))

We can now whip out write several programs over list-of-nums-and-syms:


;; cook-time : lons -> num
;; sum up all the numbers in the list

(define (cook-time a-lons)
    (cond [(empty? a-lons) ..]
        [(symbol? (first a-lons)) ..(first a-lons)..
                                  ..(cook-time (rest a-lons)).. ]
        [(number? (first a-lons)) ..(first a-lons)..
                                  ..(cook-time (rest a-lons)).. ]))

;; ingredient-count : lons -> num
;; count how many symbols are in the list
;;
(define (ingredient-count a-lons)
    (cond [(empty? a-lons) ..]
        [(symbol? (first a-lons)) ..(first a-lons)..
                                  ..(ingredient-count (rest a-lons)).. ]
        [(number? (first a-lons)) ..(first a-lons)..
                                  ..(ingredient-count (rest a-lons)).. ]))


;; no-cook-recipe? : lons -> bool
;; returns true if there are no numbers in the list
;;
(define (no-cook-recipe? a-lons)
    (cond [(empty? a-lons) ..]
        [(symbol? (first a-lons)) ..(first a-lons)..
                                  ..(no-cook-recipe? (rest a-lons)).. ]
        [(number? (first a-lons)) ..(first a-lons)..
                                  ..(no-cook-recipe? (rest a-lons)).. ]))
Notice how similar these programs are! The structure of the data determines a large portion of the program. By using the design recipes, you can write large portions of your programs without thinking about what the programs actually do.

;; get-ingredients : lons -> list-of-symbols
;; extracts a list of the symbols in the list
;;
(define (get-ingredients a-lons)
    (cond [(empty? a-lons) ..]
        [(symbol? (first a-lons)) ..(first a-lons)..
                                  ..(get-ingredients (rest a-lons)).. ]
        [(number? (first a-lons)) ..(first a-lons)..
                                  ..(get-ingredients (rest a-lons)).. ]))

;; substitute : lons sym sym -> lons
;; replaces all uses of the first symbols with the second symbol
;;
(define (substitute a-lons old-sym new-sym)
    (cond [(empty? a-lons) ..]
        [(symbol? (first a-lons)) ..(first a-lons)..
                                  ..(substitute (rest a-lons)).. ]
        [(number? (first a-lons)) ..(first a-lons)..
                                  ..(substitute (rest a-lons)).. ]))
These last two programs return lists, rather than atomic data such as numbers and booleans. Notice that they don't look much different from the earlier programs. Again, the template drives the definition of the program.

 

Now that we've gone through all this discussion on mixed data in lists, is there another way that we could have accomplished the same thing, using techniques we've already covered? What are the pros and cons of the two techniques?

 

 

 

 

Mixed data lists are often a result of not properly encapsulating one's data. If you find yourself in a situation where a mixed data list seems to be a reasonable solution, stop and reassess whether or not your data can be encapsulated into a structure of some sort.

For instance, in the above example, a "stage" structure consisting of a time and a list of ingredients could have been defined. A recipe then simply becomes a list of these stage structures and no mixed data is required. Of course, functions on a stage would have to be written to process them. The templates used would simply be those used for normal compound data structures.

 

 

©2003 Stephen Wong