DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 

(guile-tut.info.gz) Using Guile to program in Scheme

Info Catalog (guile-tut.info.gz) Introduction (guile-tut.info.gz) Top (guile-tut.info.gz) Guile in a Library
 
 3 Using Guile to program in Scheme
 **********************************
 
 In this section I give a tutorial introduction to programming in Scheme,
 with a slant toward the interesting things that can be done in Guile.
 
   This section will try to touch on many of the interesting and cool
 aspects of Guile, showing you how new types of problems can be solved
 with Guile.  Note that using Guile as a library with `libguile.a' is
 described in its own chapter ( Guile in a Library).  Also note
 that some small examples are given in  Jump Start.
 
   To get started you need to know how to program in "Scheme" (a dialect
 of LISP).  Fortunately Scheme is a small, clean language and is not
 hard to learn.  It is also used in many undergraduate courses to
 introduce computer programming.  
 
   I will not try to teach you Scheme here (although you might end up
 learning by example), since there are many good books on the subject,
 listed in  Where to find more Guile/Scheme resources. (1)
 
 3.0.1 Hello World
 -----------------
 
 Our first program is the typical Scheme "hello world" program.  Put the
 following code in a file called `hello.scm' (this can be find in
 `examples/scheme/hello.scm').
 
      #!/usr/local/bin/guile -s
      !#
 
      (display "hello world")
      (newline)
 
   Then run guile on it.  One way to do so is to start up guile and load
 this file:
 
      <shell-prompt> guile
      guile> (load "hello")
 
   Another way is to make the file executable and execute it directly.
 Notice how Guile recognizes a `-s' option which tells it to run a
 script and then exit.  Guile also has a new type of block comment
 enclosed by `#!' and `!#', so that you can make executable Scheme
 scripts with the standard UNIX `#!' mechanism.
 
   In the given example, the first line is used to invoke the Guile
 interpreter (make sure you correct the path if you installed Guile in
 something other than /usr/local/bin).  Once Guile is invoked on this
 file, it will understand that the first line is a comment.  The comment
 is then terminated with `!#' on the second line so as to not interfere
 with the execution mechanism.
 
 3.0.2 A bunch of operations in Scheme
 -------------------------------------
 
 Here is some code you can type at the `guile>' prompt to see some of
 the Scheme data types at work (mostly lists and vectors).  I have
 inserted brief comments _before_ each line of code explaining what
 happens.
 
      ;; make a list and bind it to the symbol `ls'
      guile> (define ls (list 1 2 3 4 5 6 7))
             =>
      ;; display the list
      guile> ls
             => (1 2 3 4 5 6 7)
      ;; ask if `ls' is a vector; `#f' means it is not
      guile> (vector? ls)
             => #f
      ;; ask if `ls' is a list; `#t' means it is
      guile> (list? ls)
             => #t
      ;; ask for the length of `ls'
      guile> (length ls)
             => 7
      ;; pick out the first element of the list
      guile> (car ls)
             => 1
      ;; pick the rest of the list without the first element
      guile> (cdr ls)
             => (2 3 4 5 6 7)
      ;; this should pick out the 3rd element of the list
      guile> (car (cdr (cdr ls)))
             => 3
      ;; a shorthand for doing the same thing
      guile> (caddr ls)
             => 3
      ;; append the given list onto `ls', print the result
      guile> (append ls (list 8 9 10))
             => (1 2 3 4 5 6 7 8 9 10)
      guile> (reverse ls)
             => (7 6 5 4 3 2 1)
      ;; ask if 12 is in the list -- it obviously is not
      guile> (memq 12 ls)
             => #f
      ;; ask if 4 is in the list -- returns the list from 4 on.
      ;; Notice that the result will behave as true in conditionals
      guile> (memq 4 ls)
             => (4 5 6 7)
      ;; an `if' statement using the aforementioned result
      guile> (if (memq 4 ls)
                 (display "hey, it's true!\n")
                 (display "dude, it's false\n"))
             hey, it's true!-|
             =>
      guile> (if (memq 12 ls)
                 (display "hey, it's true!\n")
                 (display "dude, it's false\n"))
             dude, it's false-|
             =>
      guile> (memq 4 (reverse ls))
             => (4 3 2 1)
      ;; make a smaller list `ls2' to work with
      guile> (define ls2 (list 2 3 4))
      ;; make a list in which the function `sin' has been
      ;; applied to all elements of `ls2'
      guile> (map sin ls2)
             => (0.909297426825682 0.141120008059867 -0.756802495307928)
      ;; make a list in which the squaring function has been
      ;; applied to all elements of `ls'
      guile> (map (lambda (n) (* n n)) ls)
             => (1 4 9 16 25 36 49)
 
      ;; make a vector and bind it to the symbol `v'
      guile> (define v #(1 2 3 4 5 6 7))
      guile> v
             => #(1 2 3 4 5 6 7)
      guile> (vector? v)
             => #t
      guile> (list? v)
             => #f
      guile> (vector-length v)
             => 7
      ;; vector-ref allows you to pick out elements by index
      guile> (vector-ref v 2)
             => 3
      ;; play around with the vector: make it into a list, reverse
      ;; the list, go back to a vector and take the second element
      guile> (vector-ref (list->vector (reverse (vector->list v))) 2)
             => 5
      ;; this demonstrates that the entries in a vector do not have
      ;; to be of uniform type
      guile> (vector-set! v 4 "hi there")
             => "hi there"
      guile> v
             => #(1 2 3 4 "hi there" 6 7)
 
 3.0.3 Using recursion to process lists
 --------------------------------------
 
 Here are some typical examples of using recursion to process a list.
 
      ;; this is a rather trivial way of reversing a list
      (define (my-reverse l)
        (if (null? l)
            l
            (append (my-reverse (cdr l)) (list (car l)))))
      (my-reverse '(27 32 33 40))
      => (40 33 32 27)
 
 3.0.4 Processing matrices
 -------------------------
 
 Suppose you have a matrix represented as a list of lists:
 
      (define m
        (list
         (list 7 2 1 3 2 8 5 3 6)
         (list 4 1 1 1 3 8 9 8 1)
         (list 5 5 4 8 1 8 2 2 4)))
 
   Then you could apply a certain function to each element of the matrix
 in the following manner:
      ;; apply the function func to the matrix m element-by-element;
      ;; return a matrix with the result.
      (define (process-matrix m func)
        (map (lambda (l)
               (map func l))
             m))
   Notice that I have used the Scheme `map' procedure because I am
 interested in the matrix that results from the application of `func',
 rather than in the side effects associated with applying `func'.
 
   This could be invoked with `(process-matrix m sin)' or
 `(process-matrix m (lambda (x) (* x x)))'; for example:
 
      (process-matrix m (lambda (x) (* x x)))
      => ((49 4 1 9 4 64 25 9 36) (16 1 1 1 9 64 81 64 1) (25 25 16 64 1 64 4 4 16))
 
   To print a representation of the matrix, we could define a generalized
 routine:
      ;; proc is a procedure to represent the single element,
      ;; row-proc is a procedure that is invoked after each row.
      ;; Example: proc could be (lambda (x) (begin (display x) (display " ")))
      ;; and row-proc could be (lambda (l) (display "\n"))
      (define (represent-matrix m proc row-proc)
        (for-each (lambda (l)
                    (begin
                      (for-each proc l)
                      (row-proc l)))
                  m))
   
   And then invoke it with
      (represent-matrix m
                        (lambda (x) (begin (display x) (display " ")))
                        (lambda (l) (begin (display "\n"))))
      7 2 1 3 2 8 5 3 6-|
      4 1 1 1 3 8 9 8 1-|
      5 5 4 8 1 8 2 2 4-|
 
   Now we write a helper routine that uses Scheme "closures" to make
 objects with state that then receive messages to draw little squares.  
 
   But let us take it one step at a time.  I will start by showing you a
 simple example of object in Scheme.  The object I make here represents a
 cell, which could be a cell in a matrix.  The cell responds to commands
 to draw itself, to return the next cell, and so forth.  _Guile does not
 currently have a Tk interface, so I will leave the hooks for graphical
 rendering.  In a future release of Guile I will add graphical rendering
 messages to the cell object._
 
      ;; cell-object.scm: routines for creating and manipulating cell objects
 
      ;; (the-x, the-y) is the initial position of the cell.
      ;; the-color is a string representing a color; must be something Tk can grok.
      ;; square-size is the size of the square that gets drawn.
      ;; (sizex, sizey) is the size of the matrix.
      (define (MAKE-CELL the-x the-y the-color square-size sizex sizey)
        (define (get-x) the-x)
        (define (get-y) the-y)
 
        (define (set-x! new-x)
          (set! the-x new-x)
          the-x)
        (define (set-y! new-y)
          (set! the-y new-y)
          the-y)
        (define (get-color) the-color)
        (define (set-color! new-color)
          (set! the-color new-color)
          the-color)
        (define (next!)
          (set! the-x (+ the-x 1))
          (if (>= the-x sizex)
      	(begin
      	  (set! the-x 0)
      	  (set! the-y (+ the-y 1))))
      	(if (>= the-y sizey)
      	    (begin
      	      (display "CELL next!: value of y is too big; not changing it\n")
      	      (set! the-y (- the-y 1))))
      	(cons the-x the-y))
        (define (draw)
          (let* ((x0 (* the-x square-size))
      	   (y0 (* the-y square-size))
      	   (x1 (+ x0 square-size))
      	   (y1 (+ y0 square-size)))
            (display "I should draw a ")
            (display the-color)
            (display " rectangle with corners at ")
            (display x0) (display y0) (display x1) (display y1)
            ))
 
        ;; self is the dispatch procedure
        (define (self message)
          (case message
            ((x)            get-x)
            ((y)            get-y)
            ((set-x!)       set-x!)
            ((set-y!)       set-y!)
            ((color)        get-color)
            ((set-color!)   set-color!)
            ((next!)        next!)
            ((draw)         draw)
            (else (error "CELL: Unknown message -> " message))))
        ;; and now return the dispatch procedure
        self
        )
   
   What does this procedure do?  It returns another procedure (`self')
 which receives a message (x, y, set-x!, set-y!, ...)  and takes an
 action to return or modify its state.  The state consists of the values
 of variables `the-x', `the-y', `the-color' and so forth.
 
   Here are some examples of how to use MAKE-CELL and the cell object it
 creates:
      (define c (MAKE-CELL 0 0 "red" 10 7 9))
 
      ;; retrieve the x and y coordinates
      ((c 'x))
      => 0
      ((c 'y))
      => 0
      ;; change the x coordinate
      ((c 'set-x!) 5)
      => 5
      ((c 'x))
      => 5
      ;; change the color
      ((c 'color))
      => "red"
      ((c 'set-color!) "green")
      => "green"
      ((c 'color))
      => "green"
      ;; now use the next! message to move to the next cell
      ((c 'next!))
      => (6 . 0)
      ((c 'x))
      => 6
      ((c 'y))
      => 0
      ;; now make things wrap around
      ((c 'next!))
      => (0 . 1)
      ((c 'next!))
      => (1 . 1)
      ((c 'next!))
      => (2 . 1)
      ((c 'x))
      => 2
      ((c 'y))
      => 1
 
   You will notice that expressions like `(c 'next)' return procedures
 that do the job, so we have to use extra parentheses to make the job
 happen.  This syntax is rather awkward; one way around it is to define a
 `send' procedure:
 
      ;; send makes object syntax a bit easier; instead of saying
      ;;     ((my-cell 'set-x!) 4)
      ;; you can say
      ;;     (send my-cell 'set-x! 4)
      (define (send obj . args)
        (let ((first-eval (apply obj (list (car args)))))
          (if (null? (cdr args))
      	(first-eval)
      	(apply first-eval (cdr args)))))
   
   You can see that `send' passes the message to the object, making sure
 that things are evaluated the proper number of times.  You can now type:
 
      (define c2 (MAKE-CELL 0 0 "red" 10 7 9))
      (send c2 'x)
      => 0
      (send c2 'set-x! 5)
      => 5
      (send c2 'color)
      => "red"
      (send c2 'set-color! "green")
      => "green"
      (send c2 'next!)
      => (1 . 0)
      (send c2 'x)
      => 1
      (send c2 'y)
      => 0
 
   This is the simplest way of implementing objects in Scheme, but it
 does not really allow for full _object-oriented programming_ (for
 example, there is no inheritance).  But it is useful for _object-based
 programming_.
 
   Guile comes with a couple more complete object-oriented extensions to
 Scheme: these are part of slib ( Object (slib)Object. and 
 Yasos (slib)Yasos.).
 
   ---------- Footnotes ----------
 
   (1) To get started, look at the books `Simply Scheme' and `The Little
 Schemer' from that list.
 
Info Catalog (guile-tut.info.gz) Introduction (guile-tut.info.gz) Top (guile-tut.info.gz) Guile in a Library
automatically generated byinfo2html