[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 (see (slib)Object section `Object' in SLIB: the portable Scheme library and see (slib)Yasos section `Yasos' in SLIB: the portable Scheme library).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |