Types within types issues
Blitz3D Forums/Blitz3D Beginners Area/Types within types issues
| ||
Hi, I am attempting to use blitz's "type" functions to create types within types. This works very well, until I attempt to access individual types within each parent type. I believe it has something to do with the way I am reading back the data within the for / next loops. Here is a simple example: <b> Type children Field name$ End Type Type parents Field name$ Field child.children End Type ;parent steve parent.parents = New parents parent\name$="steve" parent\child = New children parent\child\name$="steve/julie" parent\child = New children parent\child\name$="steve/justin" ;parent bob parent.parents = New parents parent\name$="bob" parent\child = New children parent\child\name$="bob/mike" parent\child = New children parent\child\name$="bob/stacie" For parent = Each parents Print parent\name$ For parent\child = Each children Print " "+parent\child\name$ Next Next <b> When I execute this code, each parent contains 4 children. Apparently the 'For parent\child = each children' bit is looping through each parents child and returning everything. All i want is the specific parents child. I attempted to 'For parent\child = each parent\child\children' but this did not work. Any suggetsions would be greatly appreciated :) |
| ||
give each child a number, then use that? i'm sort of misisng the point tho, so sorry bout that :) |
| ||
Hm. One way you could do that would be to give some sort of identifier so the children knew which parent they belonged to. Right now only the parent knows which child he has created last (you're overwriting the child pointer with every new child created) and thus theres no way to do what you want with the way you're representing your data. One way would be to have an integer field in your parent type, "children" or something, which would contain a handle to a bank. I've suggested a bank because they can be dynamically allocated/resized to suit a dynamic amoutn of children. Within this bank you could either do a simple approach, of storing a unique "id" number which you then assign to each child when creating them, or you could go a step further and just store the Handle of the object in the bank. Then you could use Object later to dereference a Type handle to the children listed in the bank. But. Probably the easiest way, as long as you know how many children each parent could have, would be to have an array of types within the Parent type. ie something like this would allow for 3 kids: Type Parent Field Name$ Field Children.Child[2] End Type Type Child Field Name$ End Type Then you can create your objects like this: Dad.Parent = New Parent Dad\Name = "Jimmy" Dad\Children[0] = New Child Dad\Children[0]\Name = "Kiddo 1" Dad\Children[1] = New Child Dad\Children[1]\Name = "Kiddo 2" Dad\Children[2] = New Child Dad\Children[2]\Name = "Kiddo 3" Mom.Parent = New Parent Mom\Name = "Missus" Mom\Children[0] = New Child Mom\Children[0]\Name = "Kiddo 4" Mom\Children[1] = New Child Mom\Children[1]\Name = "Kiddo 5" Mom\Children[2] = New Child Mom\Children[2]\Name = "Kiddo 6" etc etc Then to run through all of the families.. For Folks.Parent = Each Parent Print "Parent: " + Folks\Name For counter = 0 to 2 Print "Child: " + Folks\Children[counter]\Name Next Next This is all just off the top off my head, but it should compile.. as you can see it works fairly well, except now you have to know exactly how many children the parents can have. Also, there will be wasted memory if say one parent has 3 kids and another has 5.. Of course you have to allocate your array to support as many children as any of your parents could possibly have. This is the disadvantage. Thats why the bank method I was explaining before would be better, but a bit more complex as well. It would have the advantage of being able to dynamically support as many children as you wanted within each parent, however. (This is a fairly good advantage.) And if you coded around it, you could technically store different handles of different *kinds* of types (whether for example it be a Child type, or even another Parent, or whatever you wanted) But I digress.. Let me know if that helped ;) |
| ||
I think part of the problem is illustrated by the way you have named your types. The child object is called "children", but a better name would be "child". Since although the entire collection of objects of this type can be called a collection of children, each object in this collection is still an individual "child". When you create an object of type "child", this is only one object. It is not a collection of objects. The same is true for a field inside another type: the "children" field in your parent type is really just one single child object. I see you repeatedly create a new child and assign it to this "children" field in the parent type. However, that way what you are doing is simply replacing the previously created child with a new one: the "children" field will contain only the last object you assigned to it. There is also a misunderstanding of the "For Each" command. This command will always visit *every* object of a given class, no matter when or where in your program each object was created. When you have created a number of "children" for one parent, and also a number of such objects for another parent, and also a few spare children; using "For Each Children" you will visit all of them one by one. This explains the result you got. (But it is also caused by the point I made above.) So if you want to create a list of children for every parent, you need more than just one "child" field in the parent type. You need a list of child objects. Maybe I am going to fast now, but you can create such a list by means of a "linked list" of child objects. For this, you add to the child type a field called "nextChild", and this should be of the type "Children". Now when you assign a child to a parent, you simply assign it to its Children field. Then when you want to assign a second child to the same parent, you assign the second child to the "nextChild" field of the first child. This way you are building a linked list of children objects. Here's how the child type would look: Type CHILD Field name$ Field nextChild.CHILD End Type Type parents Field name$ Field child.CHILD End Type I didn't change the name "parents" but "parent" would be better (or "PARENT", I personally like to use all-uppercase names for types). You can now change the rest of the code to use the "linked list" idea: ;parent steve parent.parents = New parents parent\name$="steve" parent\child = New CHILD parent\child\name$="steve/julie" parent\child\nextChild = New CHILD parent\child\nextChild\name$="steve/justin" parent\child\nextChild\nextChild = Null ;parent bob parent.parents = New parents parent\name$="bob" parent\child = New CHILD parent\child\name$="bob/mike" parent\child\nextChild = New CHILD parent\child\nextChild\name$="bob/stacie" parent\child\nextChild\nextChild = Null As you can see, with the second child, instead of saying "\child = new CHILD", it says "\child\nextChild = new Child". So a new CHILD is created and assigned to the "nextChild" field of the first child, building a linked list of two such objects. Also note how the linked lists are closed off by assigning "Null" to the nextChild field of the last child in the list. "Null" means there is no object. To print out the linked list of child objects for every parent, you have to walk through this linked list. For this you can use a temporary CHILD variable that you point at the objects in the list one by one, until you reach the end of the list. For parent = Each parents Print parent\name$ tempChild.CHILD = parent\child While tempChild <> NULL Print " "+tempChild\name tempChild = tempChild\nextChild Wend Next I assign the first child the parent has to the tempChild variable. If it is not NULL, I print its name. Then, the nextChild is assigned to the temporary variable. This goes on until at some moment the variable is equal to NULL (meaning there is no object). Maybe this linked list idea is all a bit heavy to start with but this (or similar techniques) is something you would need to get the kind of program you were aiming for. edit: or an array, or a bank, as Surreal suggests ;) The linked list method is great when you don't know in advance how many children you will need for each parent, you can just keep adding them. By the way, they are even more handy when you also write a set of functions for handling these lists, as obviously the above example is a bit cumbersome; you have to keep repeating "nextChild\nextChild" etcetera while in reality you wouldn't know in advance how many there are; this would have to be done in a more automatic way by a function that decides which child is the last in the list, adding a new child behind that one. |
| ||
Thanks for all of the tips! Surreal had a good idea, but unfortunatly I do not know how large my child datatypes will get. Foppy also had a great idea, and thanks for explaining types in types for me. I think that the easiest way to address this would be to use an array for the children: Type children Field name$ End Type Type parents field childIndex Field name$ End Type parent.parents = new parents dim child.children(255) ;max of 255 parents. parent\name$="Steve" parent\childIndex=1 child(parent\childIndex) = new children child(parent\childIndex)\name$="Steve/julie" child(parent\childIndex) = new children child(parent\childIndex)\name$="Steve/justin" ... Don't need to finish, the rest is implied. I think this is the easiest way to keep the data seperate, and the code simple. Thanks for the help, and let me know what you think! |
| ||
That will leave you with (partly) the same problem as before: you are creating two children, but each time assign them to the same position in the array. One "children" variable (in this case, one position in the array) is just that, a (pointer to a) single children object, and not a collection of children. I mean here: child(parent\childIndex) = new children child(parent\childIndex)\name$="Steve/julie" child(parent\childIndex) = new children child(parent\childIndex)\name$="Steve/justin" parent\childIndex will be the same number in both cases (1). So this means child(parent\childIndex) will also be the same position in your array. So if you now try to print the name of Steve's children by accessing that position of the array, what you will find there is a pointer to just the second child, "Justin". Although the first child *has* been created and is somewhere in memory, the array does not contain a pointer to it; this was overwritten by the assignment of the second child to that position. In principle this big array could be a solution to your problem. Just make the array big enough so you have virtually no limit to the number of children per parent. Although there is still a limit so you would have to check for that. And you would need to solve the above problem; your program should assign each child to a different position of the array, and keep track of which ranges of positions in the array belong to one parent. It is not obvious how to do this, as you say you don't know in advance how many children a parent will have. If you want to use an array, Surreals solution of using one array for every parent (inside the parent type) seems better than using one big array that is shared by all parents. In both cases you have to make the arrays big enough in advance. |