Lecture 3: Conditionals, Recursion and Lists


Comments follow a semicolon
Remember - all expressions in this file can be evaluated
Special Forms are not evaluated in the usual way.
(define form expr) is a shorthand way of defining a function
Boolean valued functions usually end in ?
Scheme Types.
The (if condition expr1 expr2) construction
Arithmetic in Scheme
The factorial function - an example of recursion
Scheme evaluates expressions in an Eager way.
Quoted Expressions are Compound Data.
The empty list
Making Lists.
Two Identities which hold for Scheme Lists

Comments begin with a semicolon

To insert a comment in a scheme program, just type the semicolon character. The whole line after the comment will be ignored.
             ; This is  a comment
    (+ 3 4)  ; add 3 to 4

Remember - all expressions in this file can be evaluated

You will find it worthwhile to read these notes on-line using UMASS Scheme as a browser because every Scheme expression occurring on a line by itself can be run. You can then try changing it and seeing what happens. Do not be afraid of playing with the machine - you cannot break it!

[Note - this file currently exists in two forms - the HTML version which is the one you are reading, and a (nearly) plain-vanilla ASCII form which is what you get when you use the menus in UMASS Scheme. The ASCII form is the one you should execute. The eventual plan is to modify UMASS Scheme to read HTML files as a (rather limited) Web Browser, so that only the one set of text for the course is needed]

Special Forms are not evaluated in the usual way.

As we have seen the standard way to evaluate a Scheme expression is to evaluate the function and arguments and then to apply the function to the arguments.

This rule does not hold for certain special forms. Examples of special forms we have seen so far are:

(lambda (x) (* x x)) we can think of this as having a pattern described by:
   (lambda formals body )
You can think of this as evaluating to itself, with a rule for applying it to arguments (strip off lambda and substitute...)

Defining a variable

   (define variable expression)
This special form evaluates the expression and binds the variable to the resulting value. There is a shorthand way of defining a function
   (define form body)
is a variant of the   define      special form. Here form  is in effect an expression of the form:
     (variable arg1 ....argn)
where variable is the name of the function being defined and
    arg1 ... argn
are formal parameters. For example:
    (define (square x) (* x x))
is a convenient way of defining the   square   function. However this kind of definition is only a convenience. So:
   (define  (variable arg1 ....argn) body)
is exactly equivalent to
   (define  variable
        (lambda (arg1 ....argn) body)
[Remark: in the Lambda Calculus, there is only ONE special form, namely lambda abstraction. Having a small number of special forms is mathematically desirable, since the number of different cases to consider in mathematical analysis is reduced.]

Boolean valued functions usually end in ?

Functions which return a boolean value (#t  or #f ) are often called "predicates". By convention, predicates have a name which ends in a question mark. Certain common predicates like '<' are exceptions to this rule.

Scheme Types.

Objects which can exist in an implementation of Scheme belong to exactly one basic type. A Scheme implementation that meets the IEEE standard for Scheme must provide the following basic types:
    boolean,char,null,number,pair,
    procedure,string,symbol,vector
It may provide others. The type of a given object is recognised by one of the following built-in functions.
    boolean?
    char?
    null?
    number?
    pair?
    procedure?
    string?
    symbol?
    vector?
That is these predicates recognise object types which do not overlap - there may be others, e.g. files, and, in some environments, the widgets or windows which support graphical user interfaces. In this class we shall not be concerned with the types vector, char.

Atoms

The pair datatype is commonly used in Scheme to build most complex data-structures - that is those that have sub-structures. Often, we speak of any data-object that is not a pair as being an atom.

The (if condition expr1 expr2) construction

Scheme provides conditional expressions in which a condition (an expression which can have a boolean value) or conditions are used to choose which of a number of other expressions is to be evaluated. The simplest form of conditional in Scheme is the special form:
    (if condition expr1 expr2)
It evaluates condition. Unless the condition evaluates to #f then the result of the if expression is obtained by evaluating expr1 otherwise the result is obtained by evaluating expr2. [WHY does it have to be a special form? Could it NOT be a special form? ].

Arithmetic in Scheme

Arithmetic operations are denoted by the conventional symbols:
    +
    -
    *
    /
There are also the usual algebraic and trancendental functions. They operate on integers, rationals, floating-point representation of reals, complex numbers. Try:
    (sqrt -1)
the answer is the complex number:
    0.0+1.0i
Numbers can also be compared. There is a predicate
    zero?
which recognises whether a number is zero. Comparisons between real numbers are denoted by the conventional symbols.
 < > <= >=

The factorial function - an example of recursion

The well known factorial function can be defined using the recurrence relations:
    0! = 1
    n! = n(n-1)!
These translate into scheme as follows:
(define (factorial n)
    (if (= n 0)
        1
        (* n (factorial (- n 1)))))


(factorial 5)

120
Try   (factorial 1000) , and   (/(factorial 1000) (factorial 999))).

Scheme evaluates expressions in an Eager way.

The rule that Scheme evaluates the arguments of an expression before applying the function is called eager evaluation, or call-by-value. Some functional languages, such as Haskell, are lazy, and evaluate no expression unless it is actually needed. They have a better logical structure (e.g. if   does not need to be a special form in these languages), but are hard to make efficient.

Quoted Expressions are Compound Data.

So far we have seen that Scheme has numbers, booleans and functions as objects that a program can use. These are simple data-types, which we can regard as having no internal structure to be explored. Scheme has a very simple and elegant approach to providing complex data-types: any expression can be quoted. That is to say, Scheme expressions can be regarded both as program-like-objects and as data objects The long-hand way to do this is to use the special form:
     (quote expression)
For example
     (quote (+ 3 4))
evaluates to the expression:
     (+ 3 4)
Quotation is very common, so we allow
      'expression
as a shorthand. For example:
      '(+ 3 4)

[Note that this single quote is not a special form, but a syntactic shorthand for the corresponding expression using   quote  ].

Basic list-accessing functions

Such a quoted expression can be regarded as a list of sub-expressions of which the first is the function-name and the subsequent ones are the arguments. The Scheme functions   car   and   cdr    allow us to access the components of this list.   (car list)   evaluates to the first member of the list, while   (cdr list)   evaluates to the remaining members.
       (car '(+ 3 4))
evaluates to
       +
while
       (cdr '(+ 3 4))
evaluates to
       (3 4)
and
      (car (cdr '(+ 2 3))
evaluates to
      2
As well as quoting expressions that "make sense" in Scheme, such as   '(+ 2 3)  we can also use the quote notation just to make lists. For example
     '(1 2 3 4)
is a list of four natural numbers.

You can quote numbers and other constant objects, but, since they evaluate to themselves, there is no point.

Convenient list-accessing functions

Scheme implementations provide convenience functions for accessing lists. For example

 (cadr x)  =  (car (cdr x))
 (caddr x) = (car (cdr (cdr x)))
[Note -   car  and   cdr  are funny names, derived from the assembly code of an antique IBM computer.]

The empty list

The empty list, denoted by    '()    has no elements in it. It is a distinct data-type. The predicate:
    null?
recognises the empty list. It is an error to apply the functions   car  or    cdr   to the empty list.
    (car '())

Error: PAIR NEEDED
Culprits: (),
In file: /users/users3/fac/pop/poplocal/local/Scheme/lecture3.scm

Calling sequence:
    car
This error report was prepared for Robin Popplestone
 by Jeremiah Jolt, your compile-time helper.

Making Lists.

Quotation allows us to make constant lists. However we also want to allow a program to make lists that the programmer has not explicitly created. This is done with the function   cons 
     (cons x y)
makes a list whose   car  is   x  and whose   cdr  is   y . So to make a list containing just the number   23  we do:
   (cons 23 '())

    (23)
Now let's define a function to make a "sandwich" list:
(define (sandwich x y)
    (cons x (cons y (cons x '()))))
So: (sandwich 1 2 ) evaluates to:
    (1 2 1)

Two Identities which hold for Scheme Lists

Scheme is subject to the mathematical laws:
    (car (cons x y)) = x
    (cdr (cons x y)) = y

Symbols

An isolated Scheme identifier can be quoted, for example 'a. As such, it is called a symbol, and is a member of one of the basic datatypes of Scheme.

A symbol is recognised by symbol?.

Scheme has the convention that all letters occurring in a symbol are coerced into being lower case.

  'CaT

evaluates to the symbol:

  cat

This reflects the fact that Scheme converts all identifiers to lower case (as Pascal does, and C does not).