TList question - Moving through list
BlitzMax Forums/BlitzMax Beginners Area/TList question - Moving through list
| ||
Hi, Always get frustrated with TList's. There is a command MyTList.First This moves the point to the first entry. There is also the command MyTList.Last But if I want to move forward 1 entry though the list, how do I do it without using a for... each loop. In BlitzPlus, sure there was a command like MyTList.Next but cant seem to find the same under BlitzMAX Sin |
| ||
http://www.blitzbasic.com/Community/posts.php?topic=50285 |
| ||
Thanks, but to be honest that seems quite extreme to do a simple task. Cant believe they did not add that command to BlitzMax. All I want to do is change a value at a particular entry (String TList). The MyTList.ValueAtIndex(Entry) only displays it, but I want to actually change that value at that point. I cant use for...each loop (wont go into why). Anyone able to whip up a quick example? Sin |
| ||
Local txt:Object[] = ListToArray(list) txt[index] = "hello" list = ListFromArray(txt) |
| ||
Cant believe they did not add that command to BlitzMax. It isn't possible to do it exactly the same way. Lists do much more than they did in B3D/B+. The MyTList.ValueAtIndex(Entry) only displays it, but I want to actually change that value and that point. I have no idea what you mean. Either you have the type instance you want or you don't. There's no such thing as read-only types. Type Thing Field X:Int End Type Global List:TList Global AThing:Thing=New Thing AThing.X=5 List=New TList List.AddLast(AThing) Global BThing:Thing=Thing(List.ValueAtIndex(0)) BThing.X=10 Print AThing.X |
| ||
Method Before:Object() Local link:TLink=List.FindLink(Self).PrevLink:TLink() If link<>Null Return link.Value:Object() Else Return Null EndIf End Method Method After:Object() Local link:TLink=List.FindLink(Self).NextLink:TLink() If link<>Null Return link.Value:Object() Else Return Null EndIf End Method |
| ||
Bare with me, my head hurts with this stuff. :-D What I ment is that you cant do:MyTList.ValueAtIndex(Entry) = "Hello" Thanks, but what I'm having trouble with is that fact I have a Tlist of Strings. There are no Types involved Global MyTList:TList = CreateList() For x:Int = 1 to 10 ListAddLast MyTList,String(x) Next OK, now I want to change the value at position 4 to say "bob" without using a for...each loop. I have not defined any types (as the added entries are just strings) and thats what my head cant get round. I tried the ToArray and FromArray command but it wont update my list when using the FromArray command??? Sin |
| ||
OK, I've never used the TLink command before. If I didnt want to create a method and access it directly, how would I do it? This does not work, as I dont know what to pass as the FindLink() method. Global MyTList:TList = CreateList() For x:Int = 1 To 10 ListAddLast MyTList,String(x) Next For Local I:String = EachIn MyTList Print(I) Next Local Test1:String Test1 = String(MyTList.ValueAtIndex(4)) Print "Test1 = "+Test1 Local link:TLink=MyTList.FindLink(MyTList.ValueAtIndex(4)) Test2 = String(link.value) Print "Test2 = "+Test2 WaitKey() |
| ||
http://www.blitzbasic.com/codearcs/codearcs.php?code=1584 |
| ||
Looking at the TList type I don't know why you don't just look at the pred/succ fields which point directly to the previous and next objects in the list ie mylist._succ |
| ||
I can't believe ListNext isn't a command. So many people keep having this same problem. It really should be built in even if it has to search the list. Look at ListRemove, its already known to be slow because it searches the whole list. Yet we use it anyways because its simple. |
| ||
**AngelDaniel** I dont understand. How do I use this MyList._succ you speak of? Comes up with errors in the example above. Print MyTList._succ Can you show me what you mean? *** Curtastic *** I agree 100%. I got used to BlitzPlus and then they changed it in BlitzMax. Just having a few issues adjusting. Has anyone got any tutorials on TLists and more importantly TLinks? I'm sorry, but the BlitzMax docs simply dont cut it for explanations the use of commands. Sin |
| ||
The TList object is the `header` of the linked list, and it contains a pointer to the first link in the list. Each link is, if I remember right, a `TLink` object, containing a pointer to the previous TLink and a pointer to the next TLink and a pointer to the object that you stored in the link. So you need to work from the link, not the list itself. Get the link of the object that you are interested in, which returns a TLink object, and then go myTLink._succ to get the next link or myTLink._pred to get the previous link. Note that these are internal variables so BRL can potentially change them in future. All you're doing is directly accessing the previous or next link from within a given link, which is what you want. I thought there was a method that does this? Here's what the TLink object looks like (from the linkedlist.mod module): Type TLink Field _value:Object Field _succ:TLink,_pred:TLink Rem bbdoc: Returns the Object associated with this Link. End Rem Method Value:Object() Return _value End Method Rem bbdoc: Returns the next link in the List. End Rem Method NextLink:TLink() If _succ._value<>_succ Return _succ End Method Rem bbdoc: Returns the previous link in the List. End Rem Method PrevLink:TLink() If _pred._value<>_pred Return _pred End Method Rem bbdoc: Removes the link from the List. End Rem Method Remove() _value=Null _succ._pred=_pred _pred._succ=_succ End Method End Type So either you can find the link using myLink:TLink=myTList.FindLink(), and then do nextLink=myLink._succ (or myLink._pred for previous), or you can use the method myTLink.NextLink() and myTLink.PrevLink() Also look at this code from the TList object: It get the _head field from the TList object, which is the pointer to the first TLink in the list, and then goes through all of them to find the link that you want. Method FindLink:TLink( value:Object ) Local link:TLink=_head._succ While link<>_head If link._value.Compare( value )=0 Return link link=link._succ Wend End Method I prefer to either write my own more efficient linked list or at least look at _pred and _succ rather than go via a timewasting `get` method. |
| ||
I wrote this example which uses TLink to move up/down through a list. Guidance is in the code: And a simplified / clearer example to follow: |
| ||
Thanks! That is much clearer now. TLink is pretty much the same as link lists in C++/Pascal (showing my age). I think I can work round my problem with those examples :D Cheers Sin |
| ||
Jim Brown: Hi, 1 quick question. Your example code does everything except change a current an existing value to something else. I'm assuming this is not possible, so how would I do it? link.value() = "Bob" Sin |
| ||
Sin, this should worklink._value = "Bob" Note that link._value is of type object, so you can't assign it a simple number or float or anything. Strings are objects in Blitzmax, hence why the above will work. |
| ||
As SculptureSoul says, use link._value = "blah" Revised both of my examples from above: |
| ||
I'd do it this way. Personal preference I suppose, I just find it simpler. This isn't checking for errors but all you'd need to do is check for null returns before trying to change Type contents:Global stringList:TList = New TList Local s:tString 'put some random chars in list For N = 1 To 10 s = New tString s.text = Chr$(Rand(65,97)) stringList.addLast s Next 'change value of item 4 s = tString(stringList.valueAtIndex(4)) s.text = "Replaced this item!" 'find next link and change value getNext(s).text = "Replaced the next one too!" 'output For s = EachIn stringList DebugLog s.text Next 'function to get next object in list Function getNext:tString(nLink:tString) nLink = tString(stringList.FindLink(nLink).NextLink().Value()) Return nLink End Function 'declaration Type tString Field text:String End Type |
| ||
The use of next,prev etc is fine for both string and type objects but how does FindLink work with type objects? Here's Jim's code changed to use a TTEST As we no longer have 'known' or unique link names aren't we stuck to looping through the list checking each mytest.name until we find 'Shed' again. Is this where you'd use a tmap or store a link in a type field (although won't we need to find the instance before we can use it?)? |
| ||
Tony, that code won't work as FindLink is comparing the object provided as an argument against the object stored in each link. Since you don't have "Shed" stored anywhere, but instead have a TTEST in there, it won't find anything. If you want to associate a string name (or key) to a value, you're better off using TMap's or my THashtable (http://www.blitzmax.com/codearcs/codearcs.php?code=1907) For instance, with the THashtable, you could go global Hash:THashtable = THashtable.Constructor(50) 'make a 50 element hash tale global test:ttest = new ttest test.name = "Test name" Hash.InsertEntry( test.name, test ) 'this stores the object test with the key "Test name". Of course, any string or numeric key could be used. And then, when you want to retrieve an entry, all you need to know is it's key value global ttest_return:ttest 'the basic call to get an object back is Hash.GetEntry( "key value here"), 'but we must cast the result back since the hash stores & returns objects, so the final call ends up being ttest_return = ttest(Hash.GetEntry( "Test name" )) Note that key names are case sensitive. |
| ||
Thanks SculptureOfSoul but isn't that what I said ;-)? |
| ||
Well, technically you asked it, but yeah, I suppose so. ;) |
| ||
Thanks all. Great info. Sin |