Scheme Introductory Lab

This is a team assignment. Work through the following lab. There are two portions that you need to turn in, labeled with "***":

You and your partner do not both need to submit the work, but both your names should be included in program comments.

Keep in mind that in addition to this lab, there are Scheme resources linked on the course page if you need help.

There are other questions intermixed throughout the lab for you to think about. You do not need to turn in answers to these. Ask in class if the answers aren't clear.

Part 1: Getting started in DrRacket

  1. Start up DrRacket, which is the Scheme programming environment that we'll be using. If you're in one of our department labs on the third floor of the CMC, under OS X you can find it in the Finder in /Applications/CarletonApps/Racket v5.1.3/DrRacket. You might wish to drag this icon to your dock, so you have easy access to it in the future. If you wish to install this on your own computer, visit the course Moodle page for instructions.
  2. If this is the first time you're starting up DrRacket, you'll need to choose a language. There's a drop box at the bottom where you can do so: choose "Pretty Big".

  3. You will see a pair of windows. The bottom window says "Welcome to DrRacket." That is the interactions window. This is where you can test statements to see what they will do. Try it out - in the interactions window, type

    (+ 3 5)

    This should add 3 to 5. In Scheme, + is a function. To call a function in Scheme, you place the name of the function and its arguments, separated by spaces, inside parentheses. This takes a little getting used to! In most programming languages, function calls look something like this:

    function(arg1, arg2, arg3)

    In Scheme, function calls look like this:

    (function arg1 arg2 arg3)

  4. Change your program in some way. For example, re-type it as

    (+ 3 7)

  5. Experiment with hitting the Esc key followed by either the p or the n keys, which allow you to move backwards and forwards through your command history.

Part 2: Basic Scheme Primitives

  1. In the interactions window, enter the following:

    (car '(apple orange pineapple))
    (cdr '(apple orange pineapple))
    (car (apple orange pineapple))
    

    What do car and cdr do? Why is the single quote necessary? What does it do?

  2. *** Write sequences of cars and cdrs that will pick the symbol pear out of the following expressions:

    (apple orange pear grapefruit)
    (((apple) (orange) (pear) (grapefruit)))
    (apple (orange) ((pear)) (((grapefruit))))
    

    It's ok if you have trouble getting this to work: the key aspect to this assignment is to make the effort before the next class. Turn in your best attempts.

  3. Execute the following statements. What do the functions cons, append, and list do?

    ;; This is a comment, by the way!
    (cons 'x '(1 2))
    (cons '(1 5) '(2 3))
    (append '(1) '(2 3))
    (append '(1 5) '(2 3))
    (list '1 '2 '3 '(4 5))
    
  4. Execute the following code. What do length and reverse do?

    (length '(plato socrates aristotle))
    (reverse '(plato socrates aristotle))
    (member 'socrates '(plato socrates aristotle))
    (member 'raphael '(plato socrates aristotle))
    

Part 3: Saving your code

Entering your code interactively is fun, but not a good idea for creating large programs. A better way to go is to write your code, save it, then run it. Here's how to do it.

  1. In the terminal window, make a directory to store your code for this class in. For example, you can type

    mkdir cs251

    from your home directory. Then change to this directory by typing

    cd cs251

    Create a subdirectory from here by typing

    mkdir scheme

    Then change to this directory by typing

    cd scheme

  2. Start typing in some Scheme code in the definitions window at the top of the screen. Use any of the above examples that you wish. When finished, save your program by going to the File menu, and choosing Save Definitions.

  3. Run your program by clicking on the clicking on the Run button, or by using the combination Ctrl-T.

    You should generally use this approach for entering and running Scheme code, but entering code directly into the interactions window is good for testing out quick ideas.

Part 4: Conditionals

Scheme has a number of different predicates for testing equality.
  1. Try this code:

    (equal? '(hi there) '(hi there))
    (equal? '(hi there) '(bye now))
    (equal? 3 3)
    (equal? 3 (+ 2 1))
    (equal? 3 3.0)
    (equal? 3 (/ 6 2))
    (equal? -1/2 -0.5)
    (= '(hi there) '(hi there))
    (= '(hi there) '(bye now))
    (= 3 3)
    (= 3 (+ 2 1))
    (= 3 3.0)
    (= 3 (/ 6 2))
    (= -1/2 -0.5)
    

    What kind of responses do you get? How are equal? and = different?

  2. Enter the following code:

    (if (= 8 3)
        9
        10)
    

    Modify the condition following if to get a different value to return.

    [Note that Scheme pays no attention whatsoever to how you indent your code. The above indenting is stylistically useful to see what the "if" function is doing, but you could technically indent it any way you like.]

  3. Enter the following code:

    (cond ((= 16 3) (+ 3 8))
          ((= 16 8) 12)
          (else (* 6 3)))
    

    Can you modify the 16 in the above code with some other value to change the return value? What does the cond function do?

Part 5: Defining functions

  1. In a new file, enter in the following code, save it, then run it.

    (lambda (x)
      (+ x 1))
    

    What does Scheme return? The lambda function returns a function without a name. In this case, you have defined a function that takes a parameter and adds 1 to it. Functions are also called procedures.

  2. A function without a name is useless, as it is immediately garbage collected. Try this instead:

    (define add-one
      (lambda (x)
        (+ x 1)))
    

    Save this code, run it, then type (add-one 5) in the interactions window. The define statement created a pointer, called add-one, which points to the function you just created.

  3. Try this out:

    (define another-add-one add-one)
    (another-add-one 5)
    

    At the pointer level, what is happening here? Draw a picture on this paper indicating what is happening.

  4. You can declare "local" variables in Scheme via the use of the let function. For example, try the following code:

    (define a 5)
    (define b 6)
    (define c 7)
    (define strange
      (lambda (x)
        (let ((a 1) (b 2))
          (+ x a b))))
    

    After executing this code, what are the values of a, b, and c? What about what you get when you make the call (strange 3)? Why?

Part 6: Recursion

  1. Enter and load the following function. What does it do? Why?

    (define mystery
      (lambda (L)
        (if (null? L)
            L
            (append (mystery (cdr L))
                    (list (car L))))))
    

    Note that there is no return statement here, as you would find in other languages you may know. Why? How is the return value determined in the above function?

  2. To watch your program in action with the debugger, click the "Debug" button instead of the "Run" button. Then rerun your program by typing (in the interactions window)

    (mystery '((1 2) (3 4) 5 6))
    

    A series of debugging buttons will appear at the top of the definitions window. Click "Step" repeatedly, and watch the pointer move through your code. Also watch the gray bar to the far left of the debugging buttons. DrRacket will show you the return values for functions when they are called. You can also hover your mouse over variables (hover the mouse over the variable L, for example), and DrRacket will show you those variable values to the right of the debugging buttons. You can also see the stack of function calls and variable values to the right.

    You can use the "Go" button to resume execution of your program.

  3. Modify the program as follows:

    (define mystery
      (lambda (L)
        (if (null? L)
            L
    	(begin
    	  (display L)
              (append (mystery (cdr L))
    	          (list (car L)))))))
    

    What does begin do? What does display do?

  4. *** Write a recursive function, keep-first-n, that returns a list of the first n elements in a list.

    Example:

    > (keep-first-n 3 '(a b c d e f g h i))
    (a b c)
    

    Your code should do something appropriate if n is too big or too small (negative). It doesn't matter to me precisely what it does under these circumstances, so long as it does something reasonable (doesn't crash or return complete nonsense).

    Your function should not use any other Scheme functions than those which have been introduced in this lab. [An exception: if you wish, you may use the functions for less-than (<) or greater-than (>).]

  5. *** Write a recursive function named sum that adds up all the elements in a list. For example:

    (sum '(4 5 0 1)) ---> 10
    

    Do something reasonable if the list is empty.