631 lines
20 KiB
Scheme
631 lines
20 KiB
Scheme
|
||
(load "scoops.fsl")
|
||
|
||
(define extensions
|
||
(let ((blanks (make-string 4 #\space)))
|
||
(lambda (word w) ;word=string of 1 word followed by 1 blank
|
||
;w=window
|
||
(let ((c (string-ref word 0))
|
||
(word (substring word 1 (-1+ (string-length word)))))
|
||
(case c
|
||
(#\/ ;new term
|
||
(window-set-attribute! w 'text-attributes (attr 'yellow))
|
||
(display word w)
|
||
(window-set-attribute! w 'text-attributes (attr))
|
||
(display #\space w)
|
||
#!true)
|
||
(#\@ ;emphasis
|
||
(window-set-attribute! w 'text-attributes (attr 'red))
|
||
(display word w)
|
||
(window-set-attribute! w 'text-attributes (attr))
|
||
(display #\space w)
|
||
#!true)
|
||
(#\! ;break
|
||
(fresh-line )
|
||
(display word w)
|
||
(display #\space w)
|
||
#!true)
|
||
(#\] ;break and tab
|
||
(fresh-line w)
|
||
(display blanks w)
|
||
(display word w)
|
||
(display #\space w)
|
||
#!true)
|
||
(else #!false))))))
|
||
|
||
|
||
;;; the tutorial's frames ----------------------------------------
|
||
|
||
(set! *tutorial*
|
||
(make-tutorial
|
||
'name "SCOOPS"
|
||
'writeln-extensions extensions))
|
||
|
||
|
||
(frame
|
||
initial
|
||
("This tutorial will take you through defining your own instances"
|
||
"of SCOOPS classes and manipulating the instances. When the"
|
||
"tutorial is finished you will have an opportunity to try your"
|
||
"own hand at creating and manipulating SCOOPS classes. The"
|
||
"classes for this tutorial are POINT, LINE and RECTANGLE."
|
||
"Refer to chapter 5 in the Language Reference Manual for"
|
||
"additional information on SCOOPS."))
|
||
|
||
(frame
|
||
SCOOPS
|
||
("/SCOOPS is the /SCheme /Object /Oriented /Programming /System for PC Scheme,"
|
||
"similar to the LOOPS and FLAVORS systems available on various"
|
||
"makes of Lisp machines."
|
||
"Object oriented programming"
|
||
"involves the use of /objects as abstract data types. An object"
|
||
"is comprised of /variables, which determine the local state of"
|
||
"the object, and /methods which define the object's behavior.")
|
||
()
|
||
()
|
||
()
|
||
"Introduction to SCOOPS"
|
||
("SCOOPS" "object-oriented programming"
|
||
"object" "method"))
|
||
|
||
(frame
|
||
()
|
||
("In object oriented programming, all communication with an object"
|
||
"is through /messages. Objects use their own"
|
||
"procedures, called methods, to respond to the message and perform"
|
||
"some operation. A key to object oriented programming is that the"
|
||
"system performs many tasks that the programmer has to specify in"
|
||
"other types of programming styles.")
|
||
()
|
||
()
|
||
()
|
||
()
|
||
("message" "method"))
|
||
|
||
(frame
|
||
CLASS
|
||
("In our example the first thing that needs to be done with"
|
||
"SCOOPS is to define a /class. A class contains the description"
|
||
"of one or more similar objects. An object is an /instance of a class"
|
||
"with the same form as the class from which it was made, a copy. Scheme"
|
||
"uses the special form DEFINE-CLASS to create a class. For example:")
|
||
(:data (define-class point (instvars (x 0) (y 0))) :data-eval :pp-data)
|
||
|
||
("This defines a class named POINT. Each instance of the class"
|
||
"will contain two /instance /variables called X and Y and each is"
|
||
"initialized to zero.")
|
||
()
|
||
"Defining a Class"
|
||
("class" "DEFINE-CLASS" "instance variable" "instance"))
|
||
|
||
(frame
|
||
DEFINE-POINT-CLASS
|
||
("This is a simple definition and has the disadvantage that"
|
||
"when an instance is created it cannot be manipulated. No methods"
|
||
"have been included to interact with the class. A small"
|
||
"change to the definition is necessary to allow the variables"
|
||
"to be changed.")
|
||
(:data (define-class point (instvars (x 0) (y 0))
|
||
(options settable-variables)) :data-eval :pp-data)
|
||
|
||
("What this has done is to automatically define two methods for us,"
|
||
"SET-X and SET-Y. A /method is a type of function or procedure that"
|
||
"determines the behavior of a class. We will cover"
|
||
"methods a little later.")
|
||
()
|
||
()
|
||
("method" "options"))
|
||
|
||
(frame
|
||
DESCRIBE
|
||
("Now we can use the /DESCRIBE procedure. We can see that two"
|
||
"methods have already been defined, SET-X and SET-Y. The"
|
||
"DESCRIBE procedure can be used to describe either a class"
|
||
"or an instance. For example if we describe the class \"point\""
|
||
"with the command: (DESCRIBE POINT) the output will look like:")
|
||
(:output (DESCRIBE POINT))
|
||
()
|
||
()
|
||
"The DESCRIBE procedure"
|
||
("DESCRIBE"))
|
||
|
||
(frame
|
||
()
|
||
("This tells us several things:"
|
||
"]- we're describing a class"
|
||
"]- the class has no class variables"
|
||
"(this tutorial won't be discussing them)"
|
||
"]- there are two instance variables, X and Y"
|
||
"]- two methods have been defined, SET-X and SET-Y"
|
||
"]- there are no mixins"
|
||
"]- the class is not compiled"
|
||
"]- the class is not inherited"
|
||
"]We haven't yet discussed mixins or inheritance. We will discuss those"
|
||
"later. Compiling is the next topic."))
|
||
|
||
(frame
|
||
COMPILE-CLASS
|
||
("Now that you have defined a class you should /compile it."
|
||
"We're not actually generating code here but rather setting up"
|
||
"the actual inheritance structure for a class; we'll discuss"
|
||
"inheritance more later."
|
||
"If you don't use COMPILE-CLASS, it will be compiled"
|
||
"the first time you use the"
|
||
"special form MAKE-INSTANCE. Continuing with our example:")
|
||
(:data (COMPILE-CLASS POINT) :data-eval :pp-data)
|
||
()
|
||
()
|
||
"Compiling a Class"
|
||
("compile" "COMPILE-CLASS" "inheritance"))
|
||
|
||
(frame
|
||
MAKE-INSTANCE
|
||
("To create an instance of a class you would use the special form"
|
||
"/MAKE-INSTANCE. A simple instance creation would be:")
|
||
(:data (DEFINE P1 (MAKE-INSTANCE POINT)) :data-eval :pp-data)
|
||
("What this has done is to set up the data structure in memory"
|
||
"for the instance using all defaults.")
|
||
(define-point-class)
|
||
"Creating an Instance of a Class"
|
||
("MAKE-INSTANCE" ))
|
||
|
||
(frame
|
||
SEND
|
||
("In order to change the values of X and Y we would send a message to P1"
|
||
"specifying the method we want to use to manipulate the data. For example,"
|
||
"the command:")
|
||
(:data (SEND P1 SET-X 50) :data-eval :pp-data)
|
||
("would change the value of X from 0, the initial value, to"
|
||
"50.")
|
||
(make-instance)
|
||
"Sending Messages"
|
||
("SEND"))
|
||
|
||
(frame
|
||
()
|
||
("We can use the DESCRIBE procedure to describe P1 and examine the values"
|
||
"of X and Y. This command would be: (DESCRIBE P1)")
|
||
(:output (DESCRIBE P1))
|
||
("As you can see we are told we are describing an instance. The instance"
|
||
"is of class POINT. There are no class variables."
|
||
"The instance variables are X with a value of 50"
|
||
"and Y with a value of 0. Which is what we would expect.")
|
||
()
|
||
()
|
||
("DESCRIBE"))
|
||
|
||
(frame
|
||
DEFINE-METHOD
|
||
("To define a method for a class you use the special form"
|
||
"/DEFINE-METHOD. Let's define a method to display the instances of"
|
||
"the point class we've created. For example:")
|
||
(:data (DEFINE-METHOD (POINT DRAW) () (DRAW-POINT X Y)) :data-eval :pp-data)
|
||
("What we would have to do now is to send two messages, one"
|
||
"to change the value of X or Y and another to draw the point."
|
||
"This would be fine if we only wanted to put points on the"
|
||
"screen that were the same color and didn't mind old occurrences"
|
||
"hanging around.")
|
||
()
|
||
"Defining Methods"
|
||
("DEFINE-METHOD"))
|
||
|
||
(frame
|
||
()
|
||
("First we can modify the class definition to include color. This is"
|
||
"simply adding another instance variable to be used to define the"
|
||
"color. Our class POINT could now be defined as:")
|
||
(:data (define-class point
|
||
(instvars (x 0)
|
||
(y 0)
|
||
(color 7))
|
||
(options settable-variables))
|
||
:data-eval :pp-data)
|
||
("Now we have another method defined for us, SET-COLOR. And we can"
|
||
"manipulate the COLOR variable as we have manipulated the X variable."
|
||
"The problem of having to send two messages, one to set the value and"
|
||
"the other to draw the point still exists, however."))
|
||
|
||
(frame
|
||
ACTIVE-VALUES
|
||
("We can modify the class definition to include /ACTIVE /VALUES."
|
||
"Active values are used to trigger procedure invocations whenever"
|
||
"the value of the variable is accessed or updated. The special form"
|
||
"]\"(ACTIVE <initial-value> <get-fn> <set-fn>)\" !is used. Now when"
|
||
"we use SET-X, SET-X will call the \"set-fn\" and perform whatever action"
|
||
"that method indicates and will set the X to whatever value the"
|
||
"\"set-fn\" returns. Our class definition is now:")
|
||
(:data (define-class point
|
||
(instvars (x (active 0 () move-x))
|
||
(y (active 0 () move-y))
|
||
(color (active 7 () change-color))))
|
||
:data-eval :pp-data)
|
||
("Active values are automatically gettable and settable so we don't need to"
|
||
"specify those options.")
|
||
()
|
||
"Active Values"
|
||
("active value"))
|
||
|
||
(frame
|
||
()
|
||
("Now when we send a message to P1 to set X to some"
|
||
"value, the procedure MOVE-X is called automatically."
|
||
"Of course we still need to"
|
||
"write the procedures MOVE-X, MOVE-Y and CHANGE-COLOR.")
|
||
(:data (compile-class point) :data-eval))
|
||
|
||
|
||
(frame
|
||
MOVE-Y
|
||
("For example we will define the MOVE-Y method. First we will define"
|
||
"an ERASE method to erase the previous position of the point and then"
|
||
"we will define a REDRAW method to redraw the point in its new location.")
|
||
(:data (define-method (point erase) () (set-pen-color! 'black)
|
||
(draw-point x y)) :data-eval :pp-data :fresh-line
|
||
:data (define-method (point redraw) () (set-pen-color! color)
|
||
(draw-point x y)) :data-eval :pp-data :fresh-line
|
||
:data (define-method (point move-y) (new-y) (erase) (set! y new-y)
|
||
(redraw) new-y) :data-eval :pp-data)
|
||
())
|
||
|
||
(frame
|
||
()
|
||
("The methods for MOVE-X and CHANGE-COLOR would be very similar to MOVE-Y"
|
||
"now that we have the ERASE and REDRAW methods."
|
||
"We could, if we wanted, send a message to P1 and have the"
|
||
"X value changed two ways. Either you can send a message to the"
|
||
"MOVE-X method with a new value to which to set the variable or you"
|
||
"can send a message to the SET-X method with a value and let Scheme"
|
||
"call the MOVE-X method automatically.")
|
||
(:data (define p1 (make-instance point)) :data-eval
|
||
:data (send p1 move-y -50) :data-eval :pp-data :fresh-line
|
||
:data (send p1 set-y -50) :data-eval :pp-data
|
||
:data (send p1 erase) :data-eval)
|
||
("These two calls are equivalent since SET-Y will automatically call"
|
||
"MOVE-Y.")
|
||
(ACTIVE-VALUES MOVE-Y))
|
||
|
||
(frame
|
||
INHERITANCE
|
||
("Another powerful feature of object oriented programming is"
|
||
"/inheritance. Classes can inherit variables from previously"
|
||
"defined classes. For example the class \"LINE\" can inherit the"
|
||
"variables X, Y and COLOR from \"POINT\", and only need to define"
|
||
"length and direction. For example:")
|
||
(:data (define-class line
|
||
(instvars (len (active 50 () change-length))
|
||
(dir (active 0 () change-direction)))
|
||
(mixins point))
|
||
:data-eval :pp-data)
|
||
("Remember that for active values there is no need to specify options."
|
||
"The set and get methods are automatically generated. If we had some"
|
||
"procedure to be performed by the get-function, besides returning the"
|
||
"current value, then we could"
|
||
"specify a method to be executed automatically by substituting the"
|
||
"name where the \"()\" is before the set-function name.")
|
||
()
|
||
"Inheritance"
|
||
("inheritance"))
|
||
|
||
(frame
|
||
()
|
||
("In addition to inheriting variables from other classes, methods"
|
||
"are also inherited. This means that we do not have to define an"
|
||
"erase method, we inherited it from \"POINT\". In fact the only methods"
|
||
"we have to define are CHANGE-LENGTH, CHANGE-HEIGHT and DRAW."
|
||
"We need our own draw method to draw a line instead of a point."
|
||
"The practice of writing your methods to be as general as"
|
||
"possible facilitates the inheritance feature.")
|
||
()
|
||
()
|
||
()
|
||
()
|
||
("inheritance"))
|
||
|
||
(frame
|
||
()
|
||
("Having defined the CHANGE-LENGTH and CHANGE-DIRECTION methods,"
|
||
"we could modify the LINE by sending messages to the SET-LEN"
|
||
"and SET-DIR methods. If we then decide to change LINE to be another"
|
||
"set of X and Y coordinates, instead of a length and direction,"
|
||
"we could modify CHANGE-LENGTH to calculate the new position."
|
||
"Since CHANGE-LENGTH is called automatically by SET-LEN, the user"
|
||
"code would not"
|
||
"have to be changed. It would keep sending a message to SET-LEN"
|
||
"with a new length and never know that we modified two variables and"
|
||
"changed the representation of LINE. This is another powerful"
|
||
"feature of object oriented programming, the ability to change"
|
||
"the way data is structured and yet not have to change"
|
||
"any code that uses the data."))
|
||
|
||
(frame
|
||
CONCLUSION
|
||
("You may want to print out the file /scpsdemo.s, if you haven't already"
|
||
"done so, and look at the definitions of the classes. In the file you"
|
||
"will notice that the class RECTANGLE inherits POINT's"
|
||
"variables indirectly by inheriting LINE.")
|
||
()
|
||
("Following this tutorial there is a demonstration using the class"
|
||
"RECTANGLE. During the demonstration it is not possible to go"
|
||
"backwards, only forwards.")
|
||
()
|
||
"Conclusion"
|
||
("SCPSDEMO.S file"))
|
||
|
||
|
||
;
|
||
; This is an example of using SCOOPS. Please refer to chapter 5 in the
|
||
; Language Reference Manual for TI Scheme.
|
||
;
|
||
; The first thing that needs to be done is to define classes for different
|
||
; types. We will define three types, points, lines and rectangles.
|
||
|
||
;;;
|
||
;;; Point, Line and Rectangle
|
||
;;;
|
||
|
||
;;;
|
||
;;; Class POINT
|
||
;;;
|
||
|
||
|
||
(define-class point
|
||
(instvars (x (active 0 () move-x))
|
||
(y (active 0 () move-y))
|
||
(color (active 'yellow () change-color)))
|
||
(options settable-variables
|
||
inittable-variables))
|
||
|
||
(compile-class point) ; see page 45 in the language reference manual
|
||
|
||
;;;
|
||
;;; Class LINE
|
||
;;;
|
||
|
||
(define-class line
|
||
(instvars (len (active 50 () change-length))
|
||
(dir (active 0 () change-direction)))
|
||
(mixins point) ; inherit x, y, and color from point class.
|
||
(options settable-variables))
|
||
|
||
(compile-class line)
|
||
|
||
;;;
|
||
;;; Class RECTANGLE
|
||
;;;
|
||
|
||
(define-class rectangle
|
||
(instvars (height (active 60 () change-height)))
|
||
(mixins line) ; inherit color and width (len) from line
|
||
(options settable-variables))
|
||
|
||
(compile-class rectangle)
|
||
|
||
; In order to have an occurance of a class you will need to use the
|
||
; MAKE-INSTANCE procedure. For example:
|
||
; (define p1 (make-instance point))
|
||
; Then to change parts of the class use the send function. For example
|
||
; to change the color of the point previously defined:
|
||
; (send p1 change "color" 'cyan)
|
||
;
|
||
|
||
;;;
|
||
;;; Methods for POINT
|
||
;;;
|
||
|
||
(define-method (point erase) ()
|
||
(set-pen-color! 'black)
|
||
(draw))
|
||
|
||
(define-method (point draw) ()
|
||
(draw-point x y))
|
||
|
||
; having both a draw and redraw function here may seem to be unnecessary.
|
||
; you will see why both are needed as we continue
|
||
|
||
(define-method (point redraw) ()
|
||
(set-pen-color! color)
|
||
(draw))
|
||
|
||
(define-method (point move-x) (new-x)
|
||
(erase)
|
||
(set! x new-x)
|
||
(redraw)
|
||
new-x)
|
||
|
||
(define-method (point move-y) (new-y)
|
||
(erase)
|
||
(set! y new-y)
|
||
(redraw)
|
||
new-y)
|
||
|
||
(define-method (point change-color) (new-color)
|
||
(erase)
|
||
(set! color new-color)
|
||
(redraw)
|
||
new-color)
|
||
;;;
|
||
;;; Methods for LINE
|
||
;;;
|
||
|
||
; inherit erase, redraw, move-x, move-y and change-color from point.
|
||
|
||
(define-method (line draw) ()
|
||
(position-pen x y)
|
||
(draw-line-to (truncate (+ x (* len (cos dir))))
|
||
(truncate (+ y (* len (sin dir))))))
|
||
|
||
(define-method (line change-length) (new-length)
|
||
(erase)
|
||
(set! len new-length)
|
||
(redraw)
|
||
new-length)
|
||
|
||
(define-method (line change-direction) (new-dir)
|
||
(erase)
|
||
(set! dir new-dir)
|
||
(redraw)
|
||
new-dir)
|
||
|
||
;;;
|
||
;;; Methods for RECTANGLE
|
||
;;;
|
||
|
||
; inherit erase, redraw, move-x, move-y and change-color from point.
|
||
|
||
(define-method (rectangle draw) ()
|
||
(position-pen x y)
|
||
(draw-line-to (+ x len) y)
|
||
(draw-line-to (+ x len) (+ y height))
|
||
(draw-line-to x (+ y height))
|
||
(draw-line-to x y))
|
||
|
||
(define-method (rectangle change-height) (new-height)
|
||
(erase)
|
||
(set! height new-height)
|
||
(redraw)
|
||
new-height)
|
||
|
||
;
|
||
;these are routines necessary for the last part of the tutorial
|
||
;
|
||
|
||
(define small
|
||
(lambda ()
|
||
(let ((video 3)) ;this var is unused now
|
||
(set! *user-error-handler*
|
||
(lambda x
|
||
(display "There was an error. Please try again.")
|
||
(reset)))
|
||
(set-video-mode! 4)
|
||
(window-clear 'console)
|
||
(window-set-position! 'console 20 0)
|
||
(window-set-size! 'console 4 80)
|
||
(clear-graphics)
|
||
(if (equal? pcs-machine-type 1)
|
||
(begin ; for TI machines
|
||
(position-pen -360 -138)
|
||
(draw-box-to 359 -90))
|
||
(begin ; for IBM
|
||
(if (equal? (get-video-mode) 6)
|
||
(begin ; 640 x 200
|
||
(position-pen -320 -60)
|
||
(draw-line-to 319 -60))
|
||
(begin ; 320 x 200
|
||
(position-pen -160 -60)
|
||
(draw-line-to 159 -60)))))
|
||
video)))
|
||
|
||
(define finished
|
||
(lambda ()
|
||
(window-set-position! 'console 0 0)
|
||
(window-set-size! 'console 24 80)
|
||
(window-clear 'console)
|
||
(clear-graphics)
|
||
(set! *user-error-handler* nil)
|
||
(set-video-mode! 3)
|
||
))
|
||
|
||
(define pause
|
||
(lambda ()
|
||
(write-char (integer->char 2))
|
||
(read-char)
|
||
(newline)))
|
||
|
||
|
||
(define demo
|
||
|
||
(letrec ((B1 (make-instance rectangle))
|
||
(B2 (make-instance rectangle))
|
||
(L1 (make-instance line))
|
||
|
||
(prompt
|
||
(lambda (no command)
|
||
(princ "[")
|
||
(princ no)
|
||
(princ "] ")
|
||
(set! command (read))
|
||
(eval command (procedure-environment demo))
|
||
(if (equal? command (list 'finished))
|
||
0
|
||
(prompt (1+ no) command)))))
|
||
|
||
(lambda ()
|
||
|
||
(small)
|
||
|
||
(writeln " To create an instance of a class")
|
||
(writeln " use MAKE-INSTANCE. For example:")
|
||
(display " (DEFINE B1 (MAKE-INSTANCE RECTANGLE))")
|
||
(pause)
|
||
(writeln " Notice that the MAKE-INSTANCE doesn't")
|
||
(writeln " cause anything to appear on the screen.")
|
||
(writeln " All we have done so far is to define")
|
||
(display " the data strucure.")
|
||
(pause)
|
||
|
||
(writeln " To manipulate an instance we send ")
|
||
(writeln " messages to it. For example:")
|
||
(display " (SEND B1 SET-HEIGHT 40)")
|
||
(pause)
|
||
|
||
(send b1 set-height 40)
|
||
|
||
(writeln " Now let's create another instance.")
|
||
(display " (DEFINE B2 (MAKE-INSTANCE RECTANGLE))")
|
||
(pause)
|
||
|
||
(writeln " And change its x value to 100.")
|
||
(display " (SEND B2 SET-X 100)")
|
||
(pause)
|
||
|
||
(send b2 set-x 100)
|
||
|
||
(writeln " Since part of B1 was erased when we")
|
||
(writeln " moved B2, let's redraw B1.")
|
||
(display " (SEND B1 REDRAW)")
|
||
(pause)
|
||
|
||
(send b1 redraw)
|
||
|
||
(writeln " We can also change the color")
|
||
(writeln " of an instance.")
|
||
(display " (SEND B1 SET-COLOR 2)")
|
||
(pause)
|
||
|
||
(send b1 set-color 2)
|
||
|
||
(writeln " And change its width.")
|
||
(display " (SEND B2 SET-LEN 20)")
|
||
(pause)
|
||
|
||
(send b2 set-len 20)
|
||
|
||
(writeln " We can also make an instance of a line.")
|
||
(display " (DEFINE L1 (MAKE-INSTANCE LINE))")
|
||
(pause)
|
||
|
||
(writeln " With lines we can also change")
|
||
(writeln " directions, specified in radians.")
|
||
(display " (SEND L1 SET-DIR (/ 3.14 4))")
|
||
(pause)
|
||
|
||
(send l1 set-dir (/ 3.14 4))
|
||
|
||
(writeln " Of course we can also change the")
|
||
(writeln " length of the line.")
|
||
(display " (SEND L1 SET-LEN 100)")
|
||
(pause)
|
||
|
||
(send l1 set-len 100)
|
||
|
||
(writeln " Now's the time for you to try sending")
|
||
(writeln " messages on your own! You can define")
|
||
(writeln " new instances or manipulate B1, B2 and")
|
||
(display " L1.")
|
||
(pause)
|
||
(writeln " Enter (FINISHED) when you're through.")
|
||
|
||
(let ((command '()))
|
||
(prompt 1 command)))))
|
||
|
||
|