How to deal with lists of objects?

Started by oofoe, April 26, 2014, 12:14:28 AM

Previous topic - Next topic

oofoe

Hello!



I am trying to set up a little simulation project, but I am running into some problems with FOOPS and pass by value...



I have a Plane class, which considers the engine RPM and the altitude of the airplane:

(new Class 'Plane)

(define (Plane:Plane rpm alt)
(list 'Plane rpm alt))


Now, I might have methods that change the internal state of a particular instance of the Plane object. Here is one that firewalls the engine (goes to full throttle for a climb):
(define (Plane:firewall)
(setf (self 1) 2700))


This seems to work well enough for a single named instance of Plane:
(setq aplane (Plane 1900 2000))
(println "Original: " aplane)
(:firewall aplane)
(println "Modified: " aplane)


Which produces:
Original: (Plane 1900 2000)
Modified: (Plane 2700 2000)


However, if I try to create a list of many airplanes:
(setq
planes (list
(Plane 2700 0)
(Plane 2100 1250)
(Plane 1500 900)))

(println "Original: " planes)
(dolist (p planes) (:firewall p))
(println "Modified: " planes)


This is what happens:
Original: ((Plane 2700 0) (Plane 2100 1250) (Plane 1500 900))
Modified: ((Plane 2700 0) (Plane 2100 1250) (Plane 1500 900))


As you can see, nothing is changed. Upon further investigation (printing "p" after the :firewall call) I see that the intermediate object p is changed, but not stored to the list "planes". I assume that this is happening because as dolist iterates through planes, each Plane object is copied to p.



What is the best way to handle updating an object in a list of objects like this?



Also, for what it's worth, I have considered modifying :firewall to return self so I can do this:
(dotimes (i (length planes))
(setf (planes i) (:firewall (planes i))))


But I hope you can tell me there's a better way to handle it!



Thanks!
Testing can show the presence of bugs, but not their absence.

hugh.jf.chen

#1
You need to modify the firewall function to return a new complete object with the modified value, and then set the original object to the returned new object.
 
(define (Plane:firewall)
  (list Plane 2700 (self 2)))


Then,

(set 'planes (map (curry : firewall) planes))


You will get:

> planes
((Plane 2700 0) (Plane 2700 1250) (Plane 2700 900))

oofoe

#2
OK, that's basically what I was suggesting in the last para, except that my modification looked like this:
(define (Plane:firewall)
(setf (self 1) 2700) (self))


It doesn't seem very efficient to have to replace the entire data structure each time I want to do an update. OOP may not be a good fit for this in NewLISP.



The approach I'm using is typical in other languages for dealing with particle simulations and the like. Maybe I should just use a list of lists and a double setf? However, the idea of OOP-style behaviour is very attractive.



Thanks for looking at it!
Testing can show the presence of bugs, but not their absence.

m i c h a e l

#3
When a method does not return a specific value, I usually return self. This was (is?) considered good practice when programming in Smalltalk. It allows you to chain method calls together. As to how inefficient it is, well, Lutz will have to be the one to address that.



m i c h a e l

Lutz

#4
Use time function to find out what is more efficient. Style wise I could live with both.