Chapter 10

Introduction to Scheme

0. Table of Contents

        1. Working With DrScheme
        2. Primitive Data Types
        3. List Processing
        4. Memory Modification
        5. Comparing and Testing
        6. Boolean Expressions
        7. User Input and Output
        8. Conditional Statements   
        9. Iteration and Recursion
       10. Global and Local Variables
       11. Functional Programming
       12. Alternative Iteration
       13. Files and Directories
       14. Reading and Writing Files

1. Working With DrScheme

We'll be using the DrScheme programming environment in class and we
recommend that for your personal computer you download the latest
version appropriate for your operating system from the DrScheme web
site (http://www.drscheme.org/).  Unless indicated otherwise make sure
that your DrScheme 'Language' is set to 'Pretty Big'.  To do so click
on the 'Language' menu in DrScheme and you'll find 'Pretty Big' under
'Professional Languages' (you'll have to click on the 'PLT' tab to
show the 'PLT' family of languages).  In 'Pretty Big' you'll find that
'first' and 'rest' are not defined.  So, either use 'car' and 'cdr' or
define 'first' and 'rest'.  To do the latter, just include the
following two definitions in your code.

(define first car) 

(define rest cdr)

The presentation in this missive assumes that you've read Chapter 5:
'Computational Muddles' of 'Talking With Computers'.


2. Primitive Data Types

Symbols: any sequence of characters including at least one character
other than a digit.  There are some exceptions: you can't include the
single quote, double quote, back quote or any parentheses {},[],().
In addition, the first character of a symbol can't be a sharp sign #.
To define a symbol 'name' to have the value of some 'expression', type
(define name expression).  Try variations on the following:

> (define foo 1)
> foo
1
> (define foo_bar 2)
> foo_bar
2
> (define 1_foo 3)
> 1_foo
3
> (define n (+ 1 2 3))
> n
6

If you type a defined symbol to Scheme, it will respond by printing the
'value' of the symbol.  If you type a symbol that hasn't been defined, 
Scheme will complain:

> bar
reference to undefined identifier: bar

So far we've been defining symbols to have values corresponding to
integers.  There are other sorts of numbers available in Scheme
including rationals, real numbers in decimal notation and various
kinds of floating point numbers:

> (define third 1/3)
> third
1/3

Another primitive data type is the string; in Scheme, strings are
always enclosed in double quotes:

> (define my_name "Tom Dean")
> my_name
"Tom Dean"

Unlike symbols that have values assigned to them by 'define'.  Strings
and numbers always evaluate to themselves:

> "Vanessia Wu"
"Vanessia Wu"
> 2.712
2.712

The single quote is shorthand for the 'quote' function. The quote
function suppresses evaluation.  The following two invocations are 
equivalent:

> 'foo
foo
> (quote foo)
foo

Note that by quoting a symbol you can refer to the symbol rather
than any value that it might have; you can also refer to a symbol
that doesn't have a defined value.  Every symbol has a name but
not all symbols have values. 


3. List Processing

One of the most important data types in Scheme is the list; lists are
used to store information, create more complex data structures and
even represent programs.  The 'list' function takes any number of
arguments and constructs a list whose elements correspond to the
values of its arguments:

> (list 1 2 3)
(1 2 3)
> (define ones (list "one" 1 'one ))
> ones
("one" 1 one)
> (define twos (list (+ 1 1) (/ 6 3) (/ (* 2 3) 2)))
> twos
(2 2 2)

Reflect on the last two definitions; the 'list' function evaluates its
arguments (arguments are like the words to the right of a shell
command).  The strings and the numbers evaluated to themselves and we
used to the quote to suppress the evaluation of the symbol 'one'.  You
can 'nest' lists simply by calling the 'list' function as one of the
arguments to 'list':

> (define nest (list 1 2 (list 3 4) 5 6))
> nest
(1 2 (3 4) 5 6)

You can also create lists using quote; in this case, the resulting 
list is specified literally and no evaluation takes place:

> (define nums '(1 2 3 4))

Notice what happens when we incorrectly try to create a nested list
inside a list specified with quote:

> (define nest '(1 2 (list 3 4) 5 6))
> nest
(1 2 (list 3 4) 5 6)

To achieve the intended effect we just specify what we want literally:

> (define nest '(1 2 (3 4) 5 6))
> nest
(1 2 (3 4) 5 6)

It doesn't do much good to create lists if you can't subsequently take
them apart and access their contents.  The most basic functions for
accessing lists are called 'car' and 'cdr'. These names refer to
locations called 'registers' in the memory of a now defunct computer.
You can use these name if you like but you can also give them more
mnemonic names. The 'car' of a list is the first element of the list
and the 'cdr' of a list is the list consisting of all the rest of the
elements of the list but the first one.  As mentioned in class, often
programmers define 'first' and 'rest' as aliases for 'car' and 'cdr':

> (define first car)
> (define rest cdr)

What do think the value of 'car' is? 

> car
#<primitive:car>
> cdr
#<primitive:cdr>

This indicates that 'car' and 'cdr' are defined as primitive
functions.  And now we've defined 'first' and 'rest' to have the same
definitions as 'car' and 'cdr' respectively.

> first
#<primitive:car>
> rest
#<primitive:cdr>

Now we can use these functions to access the contents of lists:

> (define nums '(1 2 (3 4) 5 6))
> nums
(1 2 (3 4) 5 6)
> (first nums)
1
> (rest nums)
(2 (3 4) 5 6)

We can use various combinations of 'first' and 'rest' to access any 
element or sublist of given list.

For example, suppose that you want to grab the 4 in '(0 (1 (3 4)) 5).

First we'll do it in several steps:

> (rest '(0 (1 (3 4)) 5))      <<< step 1
((1 (3 4)) 5)
> (first '((1 (3 4)) 5))       <<< step 2
(1 (3 4))
> (rest '(1 (3 4)))            <<< step 3
((3 4))
> (first '((3 4)))             <<< step 4
(3 4)
> (rest '(3 4))                <<< step 5
(4)
> (first '(4))                 <<< step 6
4

Now we put this all together and apply these steps in the order of 
evaluation which actually looks as if we're doing it backwards or at 
least inside out, but you should convince yourself that this is 
actually in the appropriate evaluation order.

  step 6 step 5 step 4 step 3 step 2 step 1 

> (first (rest  (first (rest  (first (rest '(0 (1 (3 4)) 5)))))))
4

There's also a more primitive list constructor called 'cons' that
constructs lists in exactly the same way that 'car' ('first') and
'cdr' ('rest') take them apart.

> (define lst (cons 0 '(1 2 3)))
> lst
(0 1 2 3)
> (car lst)
1
> (cdr lst)
(1 2 3)
> (cons 0 (cons 1 (cons 2 (cons 3 ()))))
(0 1 2 3)

The 'assoc' function allows us to use so-called association lists.  An
association list is a list of pairs each pair consisting of a key and
an item associated with that key.  For example, the following
association list maps the names of fields to their offsets in a list
representing a database record.

> (define employee_fields '((id 1) (first 2) (last 3) (address 4)))

We can use 'assoc' to find the offset for a field by specifying the
field name as the key.

> (assoc 'last employee_fields)
(last 3)
> (car (cdr (assoc 'last employee_fields)))
3

Note that if the key cannot be found, 'assoc' returns false #f.

> (assoc 'middle employee_fields)
#f

For this reason, you may want to implement your field offset lookup as:

> (define (offset field table)
    (if (assoc field table)
        (car (cdr (assoc field table)))
        (printf "offset: unknown field ~a~%" field)))

See Section 7 for a discussion of 'printf'.


4. Memory Modification

The 'define' command is used to define the value of symbols.  When you
want to redefine a symbol you can use 'define' to do so, but often
once the symbol is initialized to a particular value subsequent
changes are typically made using another command called 'set!'  The
'!' is often added to commands that are said to 'destructively modify'
their arguments.  The 'set!' command is like 'set' in the C-shell
except instead of writing 'set var = expression' you write '(set! var
expression)'.  For our purposes, the first argument to 'set!' is
expected to be a symbol but, unlike symbols appearing as arguments to
other functions, this first argument is not evaluated; 'set!' works
like 'define' does in setting a symbol to have a particular value.

> (define counter 0)
> counter
0
> (set! counter (+ counter 1))
> counter 
1 
> (set! counter (+ counter 1))
> counter 
2 

There are also commands that allow you destructively alter the
structure of lists.  

> (define nums (list 1 2 3))
> (set-car! nums 7)
> nums
(7 2 3)
> (set-cdr! (cdr (cdr nums)) (list 4 5 6))
> nums
(7 2 3 4 5 6)


5. Comparing and Testing

Each of the primitive data types has a corresponding predicate for
testing membership.  A predicate is just a function that returns true
or false.  In scheme, '#f' means 'false' and '#t' means 'true'.  The
'?' is often tacked on the end of predicates to identify them as such,
but the '?' is just a convention - it's not required when defining
your own predicates.

> (number? 1)
#t
> (string? "string")
#t
> (list? '(1 2 3))
#t
> (list? "string")
#f
> (symbol? 'a)
#t

The 'string<?' predicate is a single symbol consisting of eight
characters; we might have called it 'string-less-than?' instead of
'string<?'  'string<?' is the string analog of '<' for numbers.

You can also check for special cases:

> (zero? 0)
#t
> (null? '())
#t

There are also variants of equality and inequality for every primitive 
type (substitute '>', '=', '<=', '>=' for '<' in the following to get 
the other variants). 

> (< 1 2)
#t
> (string<? "a" "b")
#t
> 

The '<' predcate is an example of a predicate that doesn't have the
'?'  tacked on the end.  Note that 'string=?', 'string<?', 'string>?',
'string<=?'  and 'string>=?' all use lexicographic order for making
comparisons whereas '=', '<', '>', '<=', '>=' use numerical order.

In Scheme '=' means numerical equality in contrast to the C-shell
where '=' is used for assignment (in 'set' and '@').  There is also an
equality predicate defined on more general types (note that for
symbols, lowercase/uppercase doesn't matter - case does matter for
strings however - Scheme can be configured so that case does matter
but the default is for it to be insensitive to case):

> (equal? 'a 'A)
#t
> (equal? 'a "a")
#f
> (equal? 1 (first (rest '(0 1 2 3))))
#t
> (equal? "a" "A")
#f
> (equal? 1 (- 2 1))
#t


6. Boolean Expressions

We can combine predicate invocations with the boolean operators:
'and', 'or', 'not'.  The first, the so-called conjunctive operator
'and' takes any number of arguments including zero it returns true 
#t if all of its arguments evaluate to #t and false #f otherwise:

> (and #t #t)
#t
> (and #t #t #f)
#f
> (and)
#t

We typically use boolean operators with predicates, e.g., 

> (and (list? arg) (not (null? (rest arg))) (number? (first arg)))

returns #t if arg is a list, has at least one element, and its first
element is a number.

The disjunctive operator 'or' works similarly; it returns #t if at
least one of its arguments evaluates to #t and false #f otherwise:

> (or #f #t #t)
#t
> (or #f)
#f
> (or)
#f

Again, the arguments typically involve predicates or nested boolean
expressions, e.g., 

> (or (and (number? arg) (> arg 0)) (symbol? arg))

returns #t if arg is a number greater than zero or a symbol.

See if you can explain why '(or) => #f' and '(and) => #t'. 

The negation operator 'not' accepts exactly one argument and returns
#t if its only argument evaluates to #f and returns #f if its only
argument evaluates to #t.

> (not #t)
#f
> (not #f)
#t
> (not (and (number? 1) (or (string? "") (null? '(1 2 3)))))
#f

Note that functions defined on strings, numbers, non-empty lists, etc., 
will complain if you call them with the wrong type of inputs:

> (rest '())
cdr: expects argument of type <pair>; given ()
> (string=? 1 "one")
string=?: expects type <string> as 1st argument, given: 1; 
          other arguments were: "one"

For this reason, it is often important to check the type of arguments
before evaluating them.  Note that in evaluating an 'and', the Scheme 
interpreter won't evaluate the second conjunct if the first conjunct 
returns #f; we can use this fact to check arguments and avoid errors of
the sort illustrated above. 

> (and (not (null? lst)) (not (null? (rest lst))) (= 1 (first (rest lst))))
#f

Here's a predicate 'my-special-list-type?' that returns #t for the
following type of data structure:

   (list symbol number (list))

and #f otherwise; so, for example: 

   > (my-special-list-type? '(a 1 ()))
   #t
   > (my-special-list-type? (list 'foo 1.2 (list)))
   #t
   > (my-special-list-type? (list 'foo 1.2))
   #f
   > (my-special-list-type? 3.14)
   #f
   > (my-special-list-type? (list 'foo 1.2))
   #f

Here's the definition of 'my-special-list-type?':


(define (my-special-list-type? arg)
  (and (list? arg) 
       (not (null? arg))
       (symbol? (first arg))
       (not (null? (rest arg)))
       (number? (first (rest arg)))
       (not (null? (rest (rest arg))))
       (null? (first (rest (rest arg))))))

Note that the first two conjuncts (list? arg) and (not (null? arg))
have to be there to make sure that the third conjunct (symbol?  (first
arg)) won't cause an error on an input like '().  The fourth conjunct
(not (null? (rest arg))) has to be there to make sure that the fifth
conjunct (number? (first (rest arg))) won't cause an error on an input
like '(1).  Similarly, the sixth conjunct prevents the seventh
conjunct from causing an error.


7. User Input and Output

The 'print', 'display' and 'newline' commands are the simplest
expedient for printing messages to the standard output.  'newline'
just outputs a newline character.  'print' and 'display' are similar
though they treat some arguments slightly different, e.g., strings:

> (display "foo") 
foo> (print "foo") 
"foo"> 

The Scheme 'printf' behaves pretty much like the C-shell 'printf'
except that we use '~%' for '\n' and directives like '~a' and '~v' to
interpolate variables into the format string, so that instead of
  
% printf "the $last number is $num\n"

you would write 

> (printf "the ~a number is ~a~%" last num)

For each appearance of '~a' in the format string there must be
one additional argument to 'printf'. 

> (define one 1)
> (define two 2)
> (define three 3)
> (printf "~a is less than ~a is less than ~a~%" one two three)
1 is less than 2 is less than 3
> (printf "~a is less than ~a~%" one)
printf: format string requires 2 arguments, given 1; 
        arguments were: "~a is less than ~a~%" 1

The '~a' directive causes its corresponding argument to be printed
using 'display' while the '-v' directive causes its corresponding
argument to be printed using 'print'.  You can learn about other 
directives using the DrScheme Help Desk (just type 'printf'). 

The 'read' function allows the user to type in an expression, a number,
string, symbol or list. 

> (define (weight) 
    (printf "Please enter your weight in pounds: ") 
    (printf "You don't weigh ~a kilograms!~%" (/ (read) 2.2)))
> (weight)
Please enter your weight in pounds: 145
You don't weigh 65.9090909090909 kilograms!

You can also use 'read' and 'print' to read from and write to files by
using 'ports'; see 'open-input-file' and 'open-output-file' to create
ports and close-input-port and 'close-output-port to close them when
you're done.


8. Conditional Statements   

Scheme conditionals are simpler and easier to use than conditionals in
the C-shell.  The 'else' clause is optional; however, if the condition
of a conditional statement lacking an 'else' clause evaluates to '#f'
then the result of the statement is undefined.

> (if (symbol? 'a) 1 2)
1
> (if (null? '(1 2)) 1)
> (if (and (number? 1) (string? "a")) 1 2)
1

There is also a generalized conditional statement 'cond' which works 
like nested if-then-else statements and is very convenient for all 
sorts of complicated tests.  Here's the general format:

   (cond ( test_1 expression_1 )
         ( test_2 expression_2 )
         ( test_3 expression_3 )
         ( test_4 expression_4 )
         ( else   expression_5 ))

   =>

   (if test_1 
       expression_1 
       (if test_2 
           expression_2 
           (if test_3 
               expression_3 
               (if test_4 
                   expression_4 
                   (if #t   
                       expression_5 )))))

Each ( test_n expression_n ) is referred to as a 'clause'.  Note that
the first test to evaluate to #t will cause its corresponding
expression to be evaluated.  The keyword 'else' evaluates to '#t' in
the context of a conditional statement.  It should only be used as the
test for the last clause - usually referred to as the 'default'
clause.  If no test evaluates to #t then the result of the 'cond'
statement is undefined.

> (define exp 1.2)
> (cond ((list? exp) (printf "It's a list!~%"))
        ((symbol? exp) (printf "It's a symbol!~%"))
        ((number? exp) (printf "It's a number!~%"))
        (else (printf "I haven't the foggiest!~%")))
It's a number!


9. Iteration and Recursion

Recursion is a way of thinking about iteration and a method for
implementing iterative algorithms.  Recursive procedures can be and
routinely are written in almost any modern programming language
including C, Java, Perl Python, etc.  For this reason, recursion
doesn't really belong as a topic in the manual for a programming
language. 

To understand recursion you have to practice solving problems with
recursion.  I suggest that you reread Section 4.1 in 'Don't Sweat the
Syntax' on how to translate specifications into implementations.  You
might also want to look at 'Recursive Definitions' on the web page at
http://www.cs.brown.edu/~tld/talk/home-Z-H-6.html#node_sec_4.2.3.  And
work through the examples and exercises in 'Calling and Creating
Functions' ../04/10/29/exercises.txt and the section entitled
'Iteration and Recursion' in ../04/11/01/exercises.txt.


10. Global and Local Variables

When you define a variable at the top level (i.e., not inside the body
of a function), that variable is said to have 'global scope'; you can
reference it anywhere including inside the body of other functions as
long as they don't have a formal parameter of the same name, e.g.,

> (define num 2.2)
> (define (scale base) (* num base))
> (define (square num) (* num num))

The function 'scale' has access to the global variable 'num' while
'square' has a formal parameter of the same name that masks the global
'num'.  Within the 'square' function 'num' refers to the local
variable introduced as its single formal parameter.

There are times however when you want to introduce a 'local' variable
(besides a formal parameter) to keep track of some information in the
midst of carrying out a computation.  This is easily accomplished by
introducing variables by way of 'let'.

> (define num 1)
> (printf "num = ~a~%" num))
num = 1
> (let ((num 0))
    (printf "num = ~a~%" num)
    (set! num 17)
    (printf "num = ~a~%" num))
num = 0
num = 17
> (printf "num = ~a~%" num))
num = 1

Inside the body of the 'let' the local variable 'num' masks the 'num'
introduced by the 'define'.  Moreover, changes to the local 'num' do
not affect the global value of 'num'.  The scope of 'num' is said to
be 'lexical' or 'static' in the sense that the scope of the local
'num' is restricted to the code within body of the 'let', i.e., (let
((num 0)) ... body ... ); what happens outside of the body of the let
doesn't affect the local value of 'num'.  If we call a function within
the body of the 'let', the definition of that function is outside the
scope of the 'let' and, unless it has a formal parameter with the
same name or introduces a local value with the same name using 'let',
changes made to the variable in the body of the function will be
reflected in the global variable:

> (define num 1)
> (printf "num = ~a~%" num)
num = 1
> (define (test arg)
    (set! num arg)
> (define (test arg)
    (set! num arg))
> (let ((num 0))
    (test 43)
    (printf "num = ~a~%" num)
    (set! num 17)
    (printf "num = ~a~%" num))
num = 0
num = 17
> (printf "num = ~a~%" num)
num = 43

Here's an example of a procedure that is difficult to write 
without the use of a local variable introduced using 'let'.

> (define pair (list 1 2))
> (define (swap pair)
    (let ((tmp (car pair)))
      (set-car! pair (car (cdr pair)))
      (set-car! (cdr pair) tmp)))
> pair
(1 2)
> (swap pair)
> pair
(2 1)


11. Functional Programming

Functional programming is a style of programming that often makes for
very simple (and elegant) solutions to complicated problems.  Suppose
that you have three columns of numbers

> (define uno  '(1 2 3))
> (define dos  '(4 5 6))
> (define tres '(7 8 9))

and you want to add the rows, i.e., the sum of the first number in
each column, the sum of the second number in each column, etc. 
Here's how you do this using a 'map' operator:

> (map + uno dos tres)
(12 15 18)

The 'map' operator takes a function (in this case '+') and one or more
lists that will serve as the arguments to the function.  'map' takes
each 'cross section' of the lists, e.g., the first cross section in
the example above would consist of the numbers 1, 4 and 7, and then
applies the function to each cross section; it then returns a list of
the results.  This next example turns rows into columns:

> (map list uno dos tres)
((1 4 7) (2 5 8) (3 6 9))

If we don't have a function that does exactly what we want we can
define an anonymous function using 'lambda'.  When you specify a
function definition using 'define' the next two expressions produce
identical results:

(define (function_name arg_1 arg_2 ... arg_n) 
  body )

(define function_name
  (lambda (arg_1 arg_2 ... arg_n) 
    body ))

However, in some cases, you want to define a special-purpose function
that will only be used for in one place in your program.  Defining
single-use functions is useful in functional programming.  Here's how
you would cube a list of numbers using 'map' and 'lambda':

> (map (lambda (x) (* x x x)) '(1 2 3 4 5 6))
(1 8 27 64 125 216)

The 'apply' function takes a function and a list of items that 
can serve as arguments to the function:

> (apply + '(1 2 3 4))
10

Suppose you have a list of numbers corresponding to the prices of the
parts for a product you're thinking of manufacturing and you want to
know the percentage that each part contributes to the total cost (this 
is also referred to as normalizing a set of numbers):

> (map (lambda (cost) (/ cost 1.0 (apply + costs))) costs)
(0.14 0.19 0.41 0.26)


12. Alternative Iteration

I certainly don't want to force you to implement iterative programs
using recursion if you find it difficult or somehow 'unnatural'.  Some
people find recursion more intuitive than 'foreach' or 'while' loops.
Perhaps it is just a matter of taste.  Scheme allows a variety of
iterative techniques besides recursion or the sort of iteration
implicit in the mapping functions common in functional programming.
One such very general iterative construct is the 'do' loop whose
general form is:

 (do (variable-initial-step-triples)
     (loop-exit-condition terminal-expressions)
   body )

where variable-initial-step-triples take the form

 ( variable initial-value step-value )

the loop-exit-condition is a test which is evaluated on each
iteration such that if the test returns true (#t) then the 
loop is terminated and the terminal-expressions are evaluated;
the loop returns the value of the last terminal expression. 

It's easier to understand if you just look at a simple example.
Here's an iterative version of factorial:

> (define (factorial n)
    (do ((i n (- i 1))
         (r 1 (* r i)))
        ((= i 1) r)))
> (factorial 4)
24

Note that 'factorial' has an empty 'body'.  The next example -
a test for primality - has one expression in its body: 

> (define (prime? n)
    (do ((factor (if (even? n) (/ n 2) (/ (- n 1) 2)) (- factor 1))
         (flag #t))
        ((or (= factor 1) (not flag)) flag)
      (if (= 0 (remainder n factor)) (set! flag #f))))
> (prime 18)
#f
> (prime 37)
#t

If you're really pining for your old 'foreach' and 'while' loops we
could simply map the 'foreach' syntax onto the 'do' loop syntax.
Here's what the mapping would look like for the 'foreach' loop:

;; foreach var ( arg )     (do ((lst arg (cdr lst)) (var #f))
;;   command_1                 ((null? lst))
;;   ...               =>    (set! var (car lst))
;;   ...                     expression_1
;;   command_n               ...
;; end                       expression_n )

To get Scheme to do the mapping for you, would use the functions
'define-syntax' and 'syntax-rules' to define a 'macro' so that
expressions beginning with 'foreach' would expand into the 'do' 
format shown above. Here's how you'd define the 'foreach' macro:

(define-syntax foreach
  (syntax-rules ()
    ((foreach var ( arg ) expr ... )
     (do ((_lst_ arg (cdr _lst_)) (var #f))
         ((null? _lst_))
       (set! var (car _lst_))
       expr ... ))))

Now we can write programs with 'foreach' loops much as we would
in C-shell scripts; there might be a few extra parentheses, but
presumably that's a small price to pay if you really miss your
'foreach' loops:

> (foreach num ( '(1 2 3 4) )
     (set! num (* num num num ))
     (printf "~a ~%" num))
1 
8 
27 
64 

We could do the same sort of thing for 'while' loops:

;; while ( test )       (do () 
;;   command_1              ((not test))
;;   command_2      =>    expression_2
;;   ...                  expression_1 
;;   command_n            ...
;; end                    expression_n )

Here's how we could get scheme to perform the mapping for us.

(define-syntax while
  (syntax-rules ()
    ((while ( test ) expr ... )
     (do () ((not test)) expr ... ))))
         
> (define num 4)
> (while ( (> num 0) )
    (printf "~a ~%" num)
    (set! num (- num 1)))
4 
3 
2 
1 


13. Loading Files and Changing Directories

Typically definitions are stored in files and 'loaded' into Scheme
using the 'load' operation.  If "functions.scm" is a file full of
'define' statements, the expression (load "functions.scm") will serve
to load and evaluate those definitions making them available for use
by other loaded functions.  You can also add 'load' statements to
other files and in this way decompose a large program into separate
components stored in separate files.  If you want to be a little more
sophisticated you might learn about 'require' but 'load' should
suffice for all your course-related projects.

At any given time, Scheme has a default directory from which files are
loaded.  You can determine or change this directory with the
'current-directory' procedure.  The 'load' procedure takes absolute or
relative path names.  If you specify a relative path name then it is
treated as relative to the directory returned by 'current-directory'.
You can also use 'file-exists?' to determine if a given file exists
and 'delete-file' to delete a file.  Here are some simple examples
illustrating 'current-directory' and 'file-exists?'.

> (current-directory)
"/u/tld"
> (current-directory "~tld/tmp/exercises/final/analysis/")
"/u/tld/tmp/exercises/final/analysis/"
> (current-directory)
"/u/tld/tmp/exercises/final/chatter/bin/"
> (file-exists? "README")
#t


14. Reading From and Writing To Files

Scheme has very sophisticated file handling routines that use what are
called 'ports'.  There are also some convenient shortcuts for writing
to and reading from files that don't require that you go through the
complicated process of opening, closing, reading from and writing to
ports.  Here are two such shortcuts that are particularly convenient:

(with-output-to-file "file.txt" 
  (lambda () 
     -procedures 'write', 'display' and 'print' will write to 
     "file.txt" without ever explicitly referring to any port-  ))

(with-input-from-file "foo.txt" 
  (lambda () 
     -the procedures 'read' and 'read-line' will read from the
     "file.txt" without ever explicitly referring to any port-  ))

> (file-exists? "foo.txt")
#f
> (with-output-to-file "foo.txt" 
    (lambda () (display "It exists now!")))
> (file-exists? "foo.txt")
#t
> (with-input-from-file "foo.txt" 
    (lambda () (read-line)))
"It exists now!"
> (delete-file "foo.txt")
> (file-exists? "foo.txt")
#f

You can have any number of reads and writes in the body of
'with-output-to-file' and 'with-input-from-file' respectively.  
You can also have 'do' loops or 'for-each' statements in the 
body of the 'lambda' expression as illustrated in the following:

> (define words '(one two three four five))
> (with-output-to-file "foo.txt" 
    (lambda () 
      (for-each 
       (lambda (word) 
         (display word) (newline)) 
       words)))
> (with-input-from-file "foo.txt" 
    (lambda () 
      (do ((word (read) (read)))
          ((eof-object? word))
        (display word) (newline))))
one
two
three
four
five

HTML conversion by TeX2page 2003-09-27