当前位置:高等教育资讯网  >  中国高校课件下载中心  >  大学文库  >  浏览文档

《Structure and Interpretation of Computer Programs》st05project4

资源类别:文库,文档格式:PDF,文档页数:27,文件大小:122.98KB,团购合买
Department of Electrical Engineering and Computer Science 6.001 Structure and Interpretation of Computer programs Spring semester 2005 Project 4-The object-Oriented Adventure Game
点击下载完整版文档(PDF)

MASSA CHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Computer Science 6.001 Structure and Interpretation of Computer programs Spring semester 2005 Project 4-The object-Oriented Adventure Game Issued: Monday. April 4th Warm-up Exercises: You may be asked to show this work in tutorial on April 11 th Design Plan: Your plan for the design exercise should be emailed to your ta by Wednesday, April 13 before 6: 00 pm, at the latest( we encourage you to do this earlier!) Project Due: Friday, April 15 before 6: 00 pm Code to load for this project o Links to the system code files objsys. scm, objtypes. scm, and setup. scm are provided from the projects link on the projects section You should begin working on the assignment once you receive it. It is to your advantage to get work done early, rather than waiting until the night before it is due. You should also read over and think through each part of the assignment(as well as any project code) before you sit down at the computer. It is generally much more efficient to test, debug, and run a program that you have thought about beforehand, rather than doing the planning"online "Diving into program development without a clear idea of what you plan to do generally causes assignments to take much longer than necessary Word to the wise: This project is difficult. The trick lies in knowing which code to write, and for that you must understand the project code, which is considerable. You'll need to understand the general ideas of object-oriented programming and the implementation provided of an object-oriented programming system(in objsys. scm). Then you'll need to understand the particular classes (in objtypes scm)and the world (in setup. scm)that we've constructed for you. In truth, this assignment in much more an exercise in reading and understanding a software system than in writing programs, because reading significant amounts of code is an important skill that you must master. The warmup exercises will require you to do considerable digesting of code before you can start on them. And we strongly urge you to study the code before you try the programming exercises themselves. Starting to program without understanding the code is a good way to get lost, and will virtually guarantee that you will spend more time on this assignment than necessary In this project we will develop a powerful strategy for building simulations of possible worlds. The strategy will enable us to make modular simulations with enough flexibility to allow us to expand and elaborate the simulation as our conception of the world expands and becomes more detailed

MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Computer Science 6.001 & Structure and Interpretation of Computer Programs Spring Semester, 2005 Project 4 - The Object-Oriented Adventure Game • Issued: Monday, April 4th • Warm-up Exercises: You may be asked to show this work in tutorial on April 11th or 12th • Design Plan: Your plan for the design exercise should be emailed to your TA by Wednesday, April 13th before 6:00 pm, at the latest (we encourage you to do this earlier!) • Project Due: Friday, April 15th before 6:00 pm • Code to load for this project: o Links to the system code files objsys.scm, objtypes.scm, and setup.scm are provided from the Projects link on the projects section. You should begin working on the assignment once you receive it. It is to your advantage to get work done early, rather than waiting until the night before it is due. You should also read over and think through each part of the assignment (as well as any project code) before you sit down at the computer. It is generally much more efficient to test, debug, and run a program that you have thought about beforehand, rather than doing the planning "online." Diving into program development without a clear idea of what you plan to do generally causes assignments to take much longer than necessary. Word to the wise: This project is difficult. The trick lies in knowing which code to write, and for that you must understand the project code, which is considerable. You'll need to understand the general ideas of object-oriented programming and the implementation provided of an object-oriented programming system (in objsys.scm). Then you'll need to understand the particular classes (in objtypes.scm) and the world (in setup.scm) that we've constructed for you. In truth, this assignment in much more an exercise in reading and understanding a software system than in writing programs, because reading significant amounts of code is an important skill that you must master. The warmup exercises will require you to do considerable digesting of code before you can start on them. And we strongly urge you to study the code before you try the programming exercises themselves. Starting to program without understanding the code is a good way to get lost, and will virtually guarantee that you will spend more time on this assignment than necessary. In this project we will develop a powerful strategy for building simulations of possible worlds. The strategy will enable us to make modular simulations with enough flexibility to allow us to expand and elaborate the simulation as our conception of the world expands and becomes more detailed

One way to organize our thoughts about a possible world is to divide it up into discrete objects, where each object will have a behavior by itself, and it will interact with other objects in some lawful way. If it is useful to decompose a problem in this way then we can construct a computational world, analogous to the"real"world, with a computational object for each real object Each of our computational objects has some independent local state, and some rules(or code), that determine its behavior. One computational object may influence another by sending it messages and invoking methods in the other. The program associated with object describes how the object reacts to messages and how its state changes as a consequence. You may have heard of this idea in the guise of" Object-Oriented Programming systems"(OOPS!). Languages such as C++ and Java are organized around OOP. While OoP has received a lot of attention in recent years, it is only one of several powerful programming styles. What we will try to understand here is the essence of the idea, rather than the incidental details of their expression in particular languages An Object system Consider the problem of simulating the activity of a few interacting agents wandering around different places in a simple world. Real people are very complicated; we do not know enough to simulate their behavior in any detail. But for some purposes(for example, to make an adventure game) we may simplify and abstract this behavior. In particular, we can use objects to capture common state parameters and behaviors of things, and can then use the message-passing paradigm to control interaction between objects in a simulation Let's start with the fundamental stuff first. We can think of our object oriented paradigm as consisting of classes and instances. A class can be thought of as the"template" for how we want a particular kind of object to behave. The way we define the class of an object with a basic"make handler"procedure, this procedure is used with a"create instance procedure which builds for us a particular instance. As you will see, when we examine the code, each class is defined by a procedure that when invoked will create some internal state(including instances of other class objects)and a message passing procedure (created by a"make handler")that returns methods in response to messages Our object instances are thus procedures which accept messages. An object will give you a method if you send it a message, you can then invoke that method (possibly with some arguments )to cause some action, state update or other computation to occur The main pieces we will use in our code to capture these ideas are detailed as follows Instance of an object- each individual object has its own identity. The instance

One way to organize our thoughts about a possible world is to divide it up into discrete objects, where each object will have a behavior by itself, and it will interact with other objects in some lawful way. If it is useful to decompose a problem in this way then we can construct a computational world, analogous to the "real" world, with a computational object for each real object. Each of our computational objects has some independent local state, and some rules (or code), that determine its behavior. One computational object may influence another by sending it messages and invoking methods in the other. The program associated with an object describes how the object reacts to messages and how its state changes as a consequence. You may have heard of this idea in the guise of "Object-Oriented Programming systems"(OOPs!). Languages such as C++ and Java are organized around OOP. While OOP has received a lot of attention in recent years, it is only one of several powerful programming styles. What we will try to understand here is the essence of the idea, rather than the incidental details of their expression in particular languages. An Object System Consider the problem of simulating the activity of a few interacting agents wandering around different places in a simple world. Real people are very complicated; we do not know enough to simulate their behavior in any detail. But for some purposes (for example, to make an adventure game) we may simplify and abstract this behavior. In particular, we can use objects to capture common state parameters and behaviors of things, and can then use the message-passing paradigm to control interaction between objects in a simulation. Let's start with the fundamental stuff first. We can think of our object oriented paradigm as consisting of classes and instances. A class can be thought of as the "template" for how we want a particular kind of object to behave. The way we define the class of an object is with a basic "make handler" procedure; this procedure is used with a "create instance" procedure which builds for us a particular instance. As you will see, when we examine the code, each class is defined by a procedure that when invoked will create some internal state (including instances of other class objects) and a message passing procedure (created by a “make handler”) that returns methods in response to messages. Our object instances are thus procedures which accept messages. An object will give you a method if you send it a message; you can then invoke that method (possibly with some arguments) to cause some action, state update, or other computation to occur. The main pieces we will use in our code to capture these ideas are detailed as follows: Instance of an object – each individual object has its own identity. The instance knows its type, and has a message handler associated with it. One can “ask” an object to do something, which will cause the object to use the message handler to

look for a method to handle the reque lest and then invoke the method on the argumen " Making " an object message handler-each instance needs a new message handler to inherit the state information and methods of the specified class. The messag handler is not a full"object instance"in our system; the message handler needs to be part of an instance object (or part of another message handler that is part of an instance object). All procedures that define classes should take a self pointer( pointer to the enclosing instance)as the first argument "Creating "an object -the act of creation does three things it makes a new instance of the object; it makes and sets the message handler for that instance; and finally it INSTALL'S that new object into the world Installing "an object -this is a method in the object, by which the object can initialize itself and insert itself into the world, by connecting itself up with other related objects in the world Let's look at these different elements in a bit more detail Classes and instances Here is the template for a class definition in our object system. This is quite similar to the one introduced in lecture, but we have cleaned up the interface a little bit to make things easier to read (define (type self argl arg2 argn (let ((superl-part (superl self args) (super2-part (super2 self args) other superclass other local state (make-handle type (make-methods message-name-2 method-2 other messages and me thods superl-part super2-part ..)) That form is a little mystifying(we have put some terms in italics to indicate that these would be replaced by specific instances), so let,'s look at an example. In our simulation almost everything is going to have a name, thus let,'s create a named-object class (def (named-object self name) let ((root-part (root-object self)))

look for a method to handle the request and then invoke the method on the arguments. “Making” an object message handler – each instance needs a new message handler to inherit the state information and methods of the specified class. The message handler is not a full “object instance” in our system; the message handler needs to be part of an instance object (or part of another message handler that is part of an instance object). All procedures that define classes should take a self pointer (a pointer to the enclosing instance) as the first argument. “Creating” an object – the act of creation does three things: it makes a new instance of the object; it makes and sets the message handler for that instance; and finally it INSTALL’s that new object into the world. “Installing” an object – this is a method in the object, by which the object can initialize itself and insert itself into the world, by connecting itself up with other related objects in the world. Let’s look at these different elements in a bit more detail. Classes and Instances Here is the template for a class definition in our object system. This is quite similar to the one introduced in lecture, but we have cleaned up the interface a little bit to make things easier to read: (define (type self arg1 arg2 ... argn ) (let ((super1-part (super1 self args) (super2-part (super2 self args) other superclasses other local state ) (make-handler type (make-methods message-name-1 method-1 message-name-2 method-2 other messages and methods ) super1-part super2-part ...))) That form is a little mystifying (we have put some terms in italics to indicate that these would be replaced by specific instances), so let's look at an example. In our simulation, almost everything is going to have a name, thus let's create a named-object class: (define (named-object self name) (let ((root-part (root-object self)))

(make-handler ed-object name of the class (make-me thods NAMe (lambda () name) INSTALL (lambda () INSTALLED DESTROY (lambda (DESTROYED) root-part)))) So we can see that this class procedure defines a template for a class. It includes some state variables(both parameters required as part of the procedure application, as well as any internal state variables we want to create ); and it creates a message handler for controlling instances of the objects. That is performed by invoking make-handler which takes as input the type of the object, a set of message-method pairs, and any inherited superclasses. Note that the message-method pairs are a combination of symbol and a procedure that will do something. Each such class procedure will be used to create instances(see below We have designed some conventions, which will be useful in following the code. We use the type of the object as the name of the procedure that defines a class(e.g. named- object in the above example). Note that this is also the first argument passed to the make-handler procedure The actual code for creating the handler is given by (define (make-handler typename methods super-parts) (cond ((not ( symbol? typename)) check for possible programmer errors (error bad typename" typename) ((not (method-list? methods) bad method list ((and super-parts (not (filter handler? super-parts))) (error "bad part list" super-parts) (named-lambda (handler message) (case message ((TYPE) ((type-extend t (lambda ( (append (me thod-names methods (lambda (x)(ask super-parts)))) else (let ((entry (method-lookup message methods))) (if entry (cadr entry) find-method-f message uper-parts)))))))))

(make-handler 'named-object ; name of the class (make-methods 'NAME (lambda () name) 'INSTALL (lambda () 'INSTALLED) 'DESTROY (lambda () 'DESTROYED)) root-part)))) So we can see that this class procedure defines a template for a class. It includes some state variables (both parameters required as part of the procedure application, as well as any internal state variables we want to create); and it creates a message handler for controlling instances of the objects. That is performed by invoking make-handler, which takes as input the type of the object, a set of message-method pairs, and any inherited superclasses. Note that the message-method pairs are a combination of a symbol and a procedure that will do something. Each such class procedure will be used to create instances (see below). We have designed some conventions, which will be useful in following the code. We use the type of the object as the name of the procedure that defines a class (e.g. named￾object in the above example). Note that this is also the first argument passed to the make-handler procedure. The actual code for creating the handler is given by: (define (make-handler typename methods . super-parts) (cond ((not (symbol? typename)) ;check for possible programmer errors (error "bad typename" typename)) ((not (method-list? methods)) (error "bad method list" methods)) ((and super-parts (not (filter handler? super-parts))) (error "bad part list" super-parts)) (else (named-lambda (handler message) (case message ((TYPE) (lambda () (type-extend typename super-parts))) ((METHODS) (lambda () (append (method-names methods) (append-map (lambda (x) (ask x 'METHODS)) super-parts)))) (else (let ((entry (method-lookup message methods))) (if entry (cadr entry) (find-method-from-handler-list message super-parts)))))))))

If you look through this code(you don t need to understand all of it! you can see that this procedure first checks for some error cases, and then in the general case creates a procedure that takes as argument a message and then checks that symbol against a set of cases(you may want to look up named-lambda in the Scheme manual to see what it does). If it is the special case of TYPE, then it returns a list of the types of objects inherited by this class. If it is the special case of METHODS, it returns a list of method names of this class, followed by the method names inherited from associated superclasses. Otherwise it tries to look up the mes sage in set of message-method pairs that were provided, and return the actual method. If there is no method for this particular class type, it then tries to look up the message in the set inherited from the superclasses Note that make-methods will build a list of (name, procedure) pairs suitable as input to make-handler (define (make-methods args (define (helper lst result) (cond ((null? lst) result error catching ((null? (car lst) (error unmatched method (name, proc) pair")) ((not (symbol? (car lst))) (if (procedure? (car lst)) 1st))) (error "invalid method name"(car lst)) ((not (procedure? (cadr lst))) (error invalid method procedure"(cadr lst)) e⊥se (hell (cddr lst) (cons (list (car lst)(cadr lst))result))))) (cons 'methods (reverse (helper args ()))) This set of code is a slightly modified version of what was presented in lecture, but with the same overall behavior. Every foo procedure defining a class of type foo takes self as the first argument This indicates of which instance the class handler is a part Returning to our definition of named-object we see that the second argument to named object is name, which is part of the state of the named-object The let statement which binds root-part to the result of making a root-object, together with the type-extend usage inside the type method of make-handler, and the use o the super-parts at the end of the definition, all together tell us that named-objects are a subclass of root-object (define (root-object self) (make-handler

If you look through this code (you don’t need to understand all of it!) you can see that this procedure first checks for some error cases, and then in the general case creates a procedure that takes as argument a message and then checks that symbol against a set of cases (you may want to look up named-lambda in the Scheme manual to see what it does). If it is the special case of TYPE, then it returns a list of the types of objects inherited by this class. If it is the special case of METHODS, it returns a list of method names of this class, followed by the method names inherited from associated superclasses. Otherwise it tries to look up the message in set of message-method pairs that were provided, and return the actual method. If there is no method for this particular class type, it then tries to look up the message in the set inherited from the superclasses. Note that make-methods will build a list of (name, procedure) pairs suitable as input to make-handler. (define (make-methods . args) (define (helper lst result) (cond ((null? lst) result) ; error catching ((null? (cdr lst)) (error "unmatched method (name,proc) pair")) ((not (symbol? (car lst))) (if (procedure? (car lst)) (pp (car lst))) (error "invalid method name" (car lst))) ((not (procedure? (cadr lst))) (error "invalid method procedure" (cadr lst))) (else (helper (cddr lst) (cons (list (car lst) (cadr lst)) result))))) (cons 'methods (reverse (helper args '())))) This set of code is a slightly modified version of what was presented in lecture, but with the same overall behavior. Every foo procedure defining a class of type foo takes self as the first argument. This indicates of which instance the class handler is a part. Returning to our definition of named-object we see that the second argument to named￾object is name, which is part of the state of the named-object. The let statement which binds root-part to the result of making a root-object, together with the type-extend usage inside the type method of make-handler, and the use of the super-parts at the end of the definition, all together tell us that named-objects are a subclass of root-object. (define (root-object self) (make-handler

(make-me thods 工s-A (lambda (type) (memg type (ask self TYPE)))))) The root object provides a basis for providing common behaviors to all classes Specifically, it provides a convenient method(Is-A)to see if a type descriptor is in the TYPE list. We will by convention use this class as the root for all other classe Named-objects have no other local state than the name variable. They do have four methods: TYPE NAME. INSTALL and DESTRoY. The TYPE method comes from the make handler procedure and it indicates that named-objects have the type named-object in ddition to any type descriptors that the root-part has. The INSTALL method is not required, but if it exists, it is called when an instance is created. In the case of named object, there is nothing to do at creation time, but we'll later see examples where this method is non-trivial. The name is a selector in that it returns the name with which the object was created However, the named-object procedure only builds a handler for an object of type named-object. In order to get an instance, we need a create-named-object procedure (define (create-named-object name) symbol - named-object (create-ins tance named-object name)) Here, an instance is created using the named-object procedure. The create-instance procedure builds a handler procedure that serves as a container for the real handler for the nstance. It also attaches a tag or label to the handler, so that we know we are working with an instance. We need this extra complexity because each foo procedure expects self as an argument, so we build an instance object, then create the handler, passing the instance object in for self. You'll explore more of this system in the questions below Using instances Once you have an instance, you can call the methods on it using the ask procedure (define book (create-named-object 'sicp)) (ask book 'NAME) alue: si (ask book : Value: (named-object root

'root (make-methods 'IS-A (lambda (type) (memq type (ask self 'TYPE)))))) The root object provides a basis for providing common behaviors to all classes. Specifically, it provides a convenient method (IS-A) to see if a type descriptor is in the TYPE list. We will by convention use this class as the root for all other classes. Named-objects have no other local state than the name variable. They do have four methods: TYPE, NAME, INSTALL, and DESTROY. The TYPE method comes from the make￾handler procedure and it indicates that named-objects have the type named-object in addition to any type descriptors that the root-part has. The INSTALL method is not required, but if it exists, it is called when an instance is created. In the case of named￾object, there is nothing to do at creation time, but we'll later see examples where this method is non-trivial. The NAME is a selector in that it returns the name with which the object was created. However, the named-object procedure only builds a handler for an object of type named-object. In order to get an instance, we need a create-named-object procedure: (define (create-named-object name) ; symbol -> named-object (create-instance named-object name)) Here, an instance is created using the named-object procedure. The create-instance procedure builds a handler procedure that serves as a container for the real handler for the instance. It also attaches a tag or label to the handler, so that we know we are working with an instance. We need this extra complexity because each foo procedure expects self as an argument, so we build an instance object, then create the handler, passing the instance object in for self. You'll explore more of this system in the questions below. Using Instances Once you have an instance, you can call the methods on it using the ask procedure: (define book (create-named-object 'sicp)) (ask book 'NAME) ;Value: sicp (ask book 'TYPE) ;Value: (named-object root)

The ask procedure retrieves the method of the given name from the instance, and then calls it. Retrieving a method from a handler is done with get-method, which ends up calling the handler procedure with the method name as the message. The specifics of the ask procedure and related procedures can be found in objsysscm Inheritance and subclasses We've already built a class, named-object, that inherited from its parent class, root object. If the handler for a named-object is sent a message that it doesn't recognize, it attempts to get a method from its parent (last line of make-handler procedure). Each handler creates a private handler for its parent to pass these messages to(the let statement in named-object). Because this parent handler is part of the same instance as the overall handler the self value is the same in both However, let's move on to a subclass of named-object called a thing. a thing is an object that will have a location in addition to a name. Thus, we may think of a thing as a kind of named object except that it also handles the messages that are special to things This arrangement is described in various ways in object-oriented jargon, e.g., "the thing class inherits from the named-object class "or"thing is a subclass of named-object d-object is a superclass of thing. (define (create-thing name location) ymbol, location - thing (create-instance thing name location ) (define (thing self name locatio (let ((named-part (named-object self name)) (make-methods INSTALL (lambda ( (ask named-part INSTALL) (ask (ask self loCation)' 'ADD-THING self) ' lOCATIoN (lambda ( location DESTROY (lambda o (ask (ask self 'loCATIoN) DEL-THING self) lambda (text) (ask screen TELL-ROOM (ask self 'LOCATION) (appen (list"At sk (ask self LOCATION)NAME) text))) named-part)) A very interesting(and confusing! property of object-oriented systems is that subclasses can specialize or override methods of their superclasses. In lecture, you saw this with professors sAying things differently than students. a subclass overrides a method on the superclass by supplying a method of the same name. For example, thing overrides the INSTALL method of named-object. When the user of the object tries to get the method

The ask procedure retrieves the method of the given name from the instance, and then calls it. Retrieving a method from a handler is done with get-method, which ends up calling the handler procedure with the method name as the message. The specifics of the ask procedure and related procedures can be found in objsys.scm. Inheritance and Subclasses We've already built a class, named-object, that inherited from its parent class, root￾object. If the handler for a named-object is sent a message that it doesn't recognize, it attempts to get a method from its parent (last line of make-handler procedure). Each handler creates a private handler for its parent to pass these messages to (the let statement in named-object). Because this parent handler is part of the same instance as the overall handler, the self value is the same in both. However, let's move on to a subclass of named-object called a thing. A thing is an object that will have a location in addition to a name. Thus, we may think of a thing as a kind of named object except that it also handles the messages that are special to things. This arrangement is described in various ways in object-oriented jargon, e.g., "the thing class inherits from the named-object class," or "thing is a subclass of named-object," or "named-object is a superclass of thing." (define (create-thing name location) ; symbol, location -> thing (create-instance thing name location)) (define (thing self name location) (let ((named-part (named-object self name))) (make-handler 'thing (make-methods 'INSTALL (lambda () (ask named-part 'INSTALL) (ask (ask self 'LOCATION) 'ADD-THING self)) 'LOCATION (lambda () location) 'DESTROY (lambda () (ask (ask self 'LOCATION) 'DEL-THING self)) 'EMIT (lambda (text) (ask screen 'TELL-ROOM (ask self 'LOCATION) (append (list "At" (ask (ask self 'LOCATION) 'NAME)) text)))) named-part))) A very interesting (and confusing!) property of object-oriented systems is that subclasses can specialize or override methods of their superclasses. In lecture, you saw this with professors SAYing things differently than students. A subclass overrides a method on the superclass by supplying a method of the same name. For example, thing overrides the INSTALL method of named-object. When the user of the object tries to get the method

named INSTALL, it will be found in thing and things version of the method will be eturned(because it never reaches the else clause in make-handler which checks the parent named-object-part). The thing class overrides two methods on named-object explicitly (as well as two implicitly); can you point out which two explicit methods are overridden One of the methods which thing overrides in an implicit manner is the TYPE method This is one of the methods that every class is supposed to override, as it allows the class to include its type descriptor in the list of types that the object has. This allows the class of an instance to be discovered at run time (define building (create-thing 'stata-center MIT)) (ask building TYPE) Value: (thing named-object root There is a handy method on the root-object called Is-A that uses the TYpe method to determine if an object has a certain type (ask building Is-A ' thing) (ask building ' IS-A named-object) (ask book 'Is-A ' thing) Value: #f (ask book Is-A named-object You' ll note that building is considered to be both a thing and a named-object, because even though it was built as a thing, things inherit from named-object Using superclass methods In the thing code the DESTRoY method uses(ask self lOCATION) in order to figure out where to remove itself from. However, it could have just referenced the location variable. It doesn't because one of the tenets of object-oriented programming is"if there 's a method that does what you need, use it. The idea is to re- use code as much reasonable. (It turns out just using location would be a bug in this case; you'll be able to see why after doing the warm-up exercises!) Some of the time, when you specialize a method, you want the subclass method to do something completely different than the superclass. For example, the way massive-stars DIE(supernova! )is very different than the way stars DIE(burn out). However, the rest of

named INSTALL, it will be found in thing and thing's version of the method will be returned (because it never reaches the else clause in make-handler which checks the parent named-object-part). The thing class overrides two methods on named-object explicitly (as well as two implicitly); can you point out which two explicit methods are overridden? One of the methods which thing overrides in an implicit manner is the TYPE method. This is one of the methods that every class is supposed to override, as it allows the class to include its type descriptor in the list of types that the object has. This allows the class of an instance to be discovered at run time: (define building (create-thing 'stata-center MIT)) (ask building 'TYPE) ;Value: (thing named-object root) There is a handy method on the root-object called IS-A that uses the TYPE method to determine if an object has a certain type: (ask building 'IS-A 'thing) ;Value: #t (ask building 'IS-A 'named-object) ;Value: #t (ask book 'IS-A 'thing) ;Value: #f (ask book 'IS-A 'named-object) ;Value: #t You'll note that building is considered to be both a thing and a named-object, because even though it was built as a thing, things inherit from named-object. Using superclass methods In the thing code, the DESTROY method uses (ask self 'LOCATION) in order to figure out where to remove itself from. However, it could have just referenced the location variable. It doesn't because one of the tenets of object-oriented programming is "if there's a method that does what you need, use it." The idea is to re-use code as much as is reasonable. (It turns out just using location would be a bug in this case; you'll be able to see why after doing the warm-up exercises!) Some of the time, when you specialize a method, you want the subclass' method to do something completely different than the superclass. For example, the way massive-stars DIE (supernova!) is very different than the way stars DIE (burn out). However, the rest of

the time, you may want to specify some additional behavior to the original. This presents a problem: how to call your superclass' method from within the overriding method D)will give rise to an infinite loop Thus, instead of asking ourself, we ask our superclass-part. Note that we do this with the INSTALL method of a thing, where we explicitly ask the superpart to also install, as well as doing some specific actions. This is the only situation in which you should be asking your superclass-part! Classes for a simulated worl When you read the code in objtypes. scm, you will see definitions of several different classes of objects that define a host of interesting behaviors and capabilities using the OOP style discussed in the previous section. Here we give a brief"tour" of some of the important classes in our simulated world Container Class Once we have things, it is easy to imagine that we might want containers for things. We can define a utility container class as shown below: (define (container self) (let ((root-part (root-object self)) (things ()) (make-handler container (make-methods I THINGS (lambda ( things) HAVE-THING? (lambda (thing) (not (null? (memg thing things)))) ADD-THING (lambda (thing) (if (not (ask self HAvE-THING? thing) (set! things (cons thing things))) DEL-THING (lambda (thing) (set! things (delg thing things)) DONE)) root-part))) Note that a container does not inherit from named-object, so it does not support messages such as NAME or INSTALL Containers are not meant to be stand-alone objects to create dure); rather, they only meant to be used internally by other objects to gain the capability of adding things, deleting things, and checking if one has something Place class

the time, you may want to specify some additional behavior to the original. This presents a problem: how to call your superclass' method from within the overriding method. Following the usual pattern of (ask self 'METHOD) will give rise to an infinite loop! Thus, instead of asking ourself, we ask our superclass-part. Note that we do this with the INSTALL method of a thing, where we explicitly ask the superpart to also install, as well as doing some specific actions. This is the only situation in which you should be asking your superclass-part! Classes for a Simulated World When you read the code in objtypes.scm, you will see definitions of several different classes of objects that define a host of interesting behaviors and capabilities using the OOP style discussed in the previous section. Here we give a brief "tour" of some of the important classes in our simulated world. Container Class Once we have things, it is easy to imagine that we might want containers for things. We can define a utility container class as shown below: (define (container self) (let ((root-part (root-object self)) (things '())) (make-handler 'container (make-methods 'THINGS (lambda () things) 'HAVE-THING? (lambda (thing) (not (null? (memq thing things)))) 'ADD-THING (lambda (thing) (if (not (ask self 'HAVE-THING? thing)) (set! things (cons thing things))) 'DONE) 'DEL-THING (lambda (thing) (set! things (delq thing things)) 'DONE)) root-part))) Note that a container does not inherit from named-object, so it does not support messages such as NAME or INSTALL. Containers are not meant to be stand-alone objects (there's no create-container procedure); rather, they are only meant to be used internally by other objects to gain the capability of adding things, deleting things, and checking if one has something. Place class

Our simulated world needs places(e.g. rooms or spaces) where interesting things will occur. The definition of the place class is shown below 1 i symbol - place (create-instance place name)) (define (place self name) (let ((named-part (named-object self name)) (container-part (container self)) exits 0)) (make-handler (make-methods EXITs (lambda exits EX工 (lambda(direction) (find-exit-in-direction exits direction)) ADD-EXIT (lambda(exit) (let ((direction (ask exit DIRECTION ))) (if (ask self 'EXIT-TOWArds direction) (error (list name "already has exit" direction)) set! exits (cons exit exits))) DONE))) container-part named-part))) If we look at the first and last lines of place, we notice that place inherits from two different classes: it has both an internal named-part and an internal container-part. If the place receives a message that doesn,'t match any of its methods, the get-method procedure will first check the container-part for the method, then use the named-part This is generally called"multiple inheritance, "which comes with a host of issues discussed briefly in lecture. You'll note that named-object and container only share one method of the same name, TYPE, and place overrides it. The TYPE method calls the type-extend procedure with both parent-parts. Retrieving the type of a place (define stata (create-place 'stata-center)) (ask stata TYPE) lue: (place named-object root container) You aren't guaranteed anything about the order of the type-descriptors except that the first descriptor in the list is the class that you instantiated to create the instance. You can also see that our place instances will each have their own internal variable exits, which will be a list of exit instances which lead from one place to another place In our object- oriented terminology, we can say the place class establishes a"has-a"relationship with the exit class(as opposed to the "is-a"relationship denoting inheritance). You should examine the objtypes. scm file to understand the definition for exits Mobile-thing class

Our simulated world needs places (e.g. rooms or spaces) where interesting things will occur. The definition of the place class is shown below. (define (create-place name) ; symbol -> place (create-instance place name)) (define (place self name) (let ((named-part (named-object self name)) (container-part (container self)) (exits '())) (make-handler 'place (make-methods 'EXITS (lambda () exits) 'EXIT-TOWARDS (lambda (direction) (find-exit-in-direction exits direction)) 'ADD-EXIT (lambda (exit) (let ((direction (ask exit 'DIRECTION))) (if (ask self 'EXIT-TOWARDS direction) (error (list name "already has exit" direction)) (set! exits (cons exit exits))) 'DONE))) container-part named-part))) If we look at the first and last lines of place, we notice that place inherits from two different classes: it has both an internal named-part and an internal container-part. If the place receives a message that doesn't match any of its methods, the get-method procedure will first check the container-part for the method, then use the named-part. This is generally called "multiple inheritance," which comes with a host of issues as discussed briefly in lecture. You'll note that named-object and container only share one method of the same name, TYPE, and place overrides it. The TYPE method calls the type-extend procedure with both parent-parts. Retrieving the type of a place: (define stata (create-place 'stata-center)) (ask stata 'TYPE) ;Value: (place named-object root container) You aren't guaranteed anything about the order of the type-descriptors except that the first descriptor in the list is the class that you instantiated to create the instance. You can also see that our place instances will each have their own internal variable exits, which will be a list of exit instances which lead from one place to another place. In our object￾oriented terminology, we can say the place class establishes a "has-a" relationship with the exit class (as opposed to the "is-a" relationship denoting inheritance). You should examine the objtypes.scm file to understand the definition for exits. Mobile-thing class

点击下载完整版文档(PDF)VIP每日下载上限内不扣除下载券和下载次数;
按次数下载不扣除下载券;
24小时内重复下载只扣除一次;
顺序:VIP每日次数-->可用次数-->下载券;
共27页,试读已结束,阅读完整版请下载
相关文档

关于我们|帮助中心|下载说明|相关软件|意见反馈|联系我们

Copyright © 2008-现在 cucdc.com 高等教育资讯网 版权所有