I have a problem with move function in my code. I need it to be :
- one function which can move all shapes or,
- multiple functions with the same name.
So far I have move functions with diffrent names for point, circle and polygon. I can't figure out how to make move function for picture.
If you guys can help me with that move function for picture and edit all the move function so they work like I described at beginning.
;
; POINT
;
(defun make-point ()
(list (list 0 0) :black))
(defun x (point)
(caar point))
(defun y (point)
(cadar point))
(defun set-x (point new-x)
(setf (caar point) new-x)
point)
(defun set-y (point new-y)
(setf (cadar point) new-y)
point)
(defun move (point dx dy)
(set-x point (+ (x point) dx))
(set-y point (+ (y point) dy))
point)
;
; CIRCLE
;
(defun make-circle ()
(list (make-point) 1 :black))
(defun center (circle)
(car circle))
(defun radius (circle)
(cadr circle))
(defun set-radius (circle new-rad)
(if (> 0 new-rad)
(format t "Polomer ma byt kladne cislo, zadali ste : ~s" new-rad)
(setf (cadr circle) new-rad))
circle)
(defun movec (circle dx dy)
(move (center circle) dx dy)
circle)
;
; POLYGON
;
(defun make-polygon ()
(list nil :black))
(defun items (shape)
(car shape))
(defun set-items (shape val)
(setf (car shape) val)
shape)
(defun movep (polygon dx dy)
(mapcar (lambda (b) (move b dx dy)) (items polygon))
polygon)
;
; PICTURE
;
(defun make-picture ()
(list nil :black))
;(defun movepi (picture dx dy))
; items, set-items used for polygon and picture
Your objects are just lists, you will have a hard time distinguishing among different kinds of shapes. You could add a keyword, a tag type, in front of your lists (e.g.
:point,:circle, etc.) to better dispatch your move operations according to that tag, but then that would be reinventing the wheel, a.k.a. objects.Simple functions and lists
You can do that, provided you can dispatch on the actual type of object you are working with.
moveshould be able to know what kind of shape is being moved. Change your data-structures if you can to add the type of object as the CAR of your lists, and use a CASE to dispatch and then move each object as needed.This is not possible, at least in the same package.
CLOS
Multiple shapes have a color, so let's define a class that represent objects which have a color component:
If you are unfamiliar with CLOS (Common Lisp Object System), the above defines a class named
has-color, with no superclass and a single slot,color. The accessor names both the reader and writer generic functions, such that you can do(color object)to retrieve an object, and(setf (color object) color)to set the color of an object to a color. The:initargis used to define the keyword argument that is to be used inmake-instance.Here below, we define a
point, which has a color and additionalxandycoordinates.The same for a circle:
And a polygon:
Finally, a picture is a sequence of shapes:
You can make a circle as follows:
You could also define shorter constructor functions, if you wanted.
Now, you can use a generic function to
moveyour objects. You first define it withDEFGENERIC, which declares the signature of the generic function, as well as additional options.Now, you can add methods to that generic function, and your generic function will dispatch to them based on one or more specializers and/or qualifiers.
For example, you move a point as follows:
You can see that we specialize
movebased on the class of the first parameter, here namedpoint. The method is applied when the value bound topointis of classpoint. The call toINCFimplicitly calls(setf x)and(setf y), defined above.Moving a circle means moving its center:
You can specialize a generic function on any class, for example the standard
SEQUENCEclass. It moves all the objects in the sequence with the same offsets:This is useful for polygons:
And also for pictures:
Immutable version
You could also make
movebuild new instances, but that requires to somehow make copies of existing objects. A simple approach consists in having a generic function which fills a target instance with a source instance:The method combination here means that all methods that satisfy
fill-copyare run, instead of only the most specific one. Theprognsuggests that all methods are run in aprognblock, one after the other. With the above definition, we can define a simplecopy-objectgeneric function:The above defines a generic function named
copy-object, as well as a default method for an object of type T (any object).ALLOCATE-INSTANCEcreates an instance but does not initialize it. The method usesFILL-COPYto copy slot values.You can for example define how to copy the
colorslot of any object that has a color:Notice that you have multiple dispatch here: both the source and target objects must be of class
has-colorfor the method to be called. Theprognmethod combination allows to distribute the job offill-copyamong different, decoupled, methods:If you give a point to
fill-copy, two methods can be applied, based on the class hierarchy ofpoint: the one defined forhas-color, and the one specialized on thepointclass (for both arguments). Theprognmethod combination ensures both are executed.Since some slots can be unbound, it is possible that
fill-copyfails. We can remedy to that by adding an error handler aroundfill-copy:The
(call-next-method)form calls the other methods (those defined by theprognqualifier), but we wrap it insideignore-errors. Here no color is defined, but the copy succeeds:We can now keep our existing, mutating,
movemethods, and wrap them in a:aroundspecialized method that first make a copy:In order to see what happens, define a method for
PRINT-OBJECT:And now, moving a point creates a new point:
You would still need to change the method for the SEQUENCE type, which currently discards the return values of
move, but apart from that there is little change to make to existing code.Note also that the above approach is mostly used as a way to describe the various uses of CLOS, and in practice you would probably choose one way or another to move points (mutable or not), or you would have different functions instead of a single generic one (e.g. mut-move and move).