The Law of Scheme

We'll take a little breather, and spend 10min giving a full explanation of everything scheme does.
Kind of expression Examples How to evaluate
Value 17, true, 'arugula You have the value already.
Placeholder pi, + Get its defined value.
Function application (+ (* 9 4) pi), (fahr->celc -40) Evaluate the subexpressions (exp0 exp1 ... expn) to get the values (val0 val1 ... valn).
  • If val0 is primitive, 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 definition parameters with the values. Evaluate the rewritten body.
Special form define, define-struct, cond; and, or various, but not shocking

Note that the first part, about placeholders, explains why we see the test output as we do, when we write (times-served empty) = 0. Note that a slightly nicer way to write your test cases could be: (= (times-served empty) 0). (What would print out?)

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.


Okay, back to functions on lists...

(.ss version)

A function which takes in a list of complex values, and returns a list:

;; count-dc10s: list-of-planes --> num
;; Given a list of planes, return how many of them are DC-10s.
;;




;; just-dc10s: list-of-planes --> list-of-planes
;; Given a list of planes, return all the ones that are DC-10s.
;;
What helper function will we want?

What if we had a list of flying-objects? What would the function look like then? (What helpers would make it real easy?) Note that you could try to draw out a picture of the nested boxes, but it gets complicated quickly, and your understanding is actually better to only look at small portions of it! (NOT a natural way of thinking; this class teaches extremely reductionist thinking. It works great for programming. It doesn't work so well for, say, political issues.)

Lists of Mixed Data

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 better 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)).. ]))
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.