FindLink doesn't work?

BlitzMax Forums/BlitzMax Beginners Area/FindLink doesn't work?

sswift(Posted 2006) [#1]
			For ThisSprite:Sprite = EachIn Sprite.SpriteList
				
				ThisLink:TLink = Sprite.SpriteList.FindLink(ThisSprite)			
				
				If ThisLink = OldLink:TLink Then RuntimeError "blah!"
				OldLink:TLink = ThisLink


This code exits with a runtimerror.
Why isn't ThisLink changing? The sprite list contains 2000 sprites, and they're displayed on the screen, so I know the list contains more than one sprite, and is set up correctly.


Azathoth(Posted 2006) [#2]
What does your compare method do?


sswift(Posted 2006) [#3]
What does compare have to do with it?

		Method Compare(OtherObject:Object)

			Local OtherSprite:Sprite = Sprite(OtherObject)
			If OtherSprite = Null Then Return 0
						
			Return Order - OtherSprite.Order
			
		End Method



assari(Posted 2006) [#4]
Sswift,
This works for me
Type sprite
 Global spritelist:TList=CreateList()
 Field x:Int

 Function create:sprite(z:Int)
	s:sprite=New sprite
	s.x=z
	ListAddLast spritelist,s
	Return s
 EndFunction

EndType

sprite.create(23)
sprite.create(1)
sprite.create(2)

For ThisSprite:Sprite = EachIn Sprite.SpriteList

	ThisLink:TLink = Sprite.SpriteList.FindLink(ThisSprite)
	Print sprite(Thislink.value()).x
	If ThisLink = OldLink:TLink Then RuntimeError "blah!"
	OldLink:TLink = ThisLink

Next

End



Azathoth(Posted 2006) [#5]
FindLink uses compare


sswift(Posted 2006) [#6]
I'm confused. It doesn't make sense for FindLink to use compare.

Findlink finds the LINK with a value.
Not the OBJECT with a value.

Compare compares the value of one OBJECT to another, not the value of one LINK to another.

The value of a LINK is a number, or a string, or a pointer to an object, in this case, my sprites.

Those sprites each have a value, which is their Order parameter. But FindLink does not look for the OBJECT with the specificed value in the list, it looks for the LINK with the specified value in the list.

And the specified value, is a sprite pointer. And the LINK with that sprite pointer, is the one it should return.


Unless I have a totally screwed up understanding of how the linked lists work, or the help file is wrong, I see no reason for compare to enter the equation.


tonyg(Posted 2006) [#7]
It finds the object with a value and returns the link to it.
Function ListFindLink:TLink( list:TList,value:Object )

Check linkedlist.bmx for the source.


Yan(Posted 2006) [#8]
...And The Swift said unto them 'Do not question the word of The Swift, for he is all Knowing'...

[/edit]
This was originally posted before SSwift edited his previous posts to be much less arrogant and condescending, who knew such a thing was possible.
[edit]


sswift(Posted 2006) [#9]
Tony:
I did look at the source. but I'll look again...

	Rem
	bbdoc: Returns the first link in the list with the given value, or null if none found.
	End Rem
	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



Okay, you're right. It does use compare. My mistake.


I would argue here though that the docs suck:
"Returns the first link in the list with the given value"

This is horrible wording.

Method AddFirst:TLink( value:Object )
	Return InsertAfterLink( value,_head )
End Method



See the parameter there? VALUE. The VALUE of a link, is a pointer to another type instance!

What findlink apparently returns is the first link in the list with a value that has the specified value.

NOT "Returns the first link in the list with the given value"

Ie:

The wording sounds like it does this:

' Returns the first link in the list with the given value.
For ThisLink = Each Link
   If Link.Value = Value then Return Link
Next


But what it actually does is this:

' Returns the first link in the list with a value that has the specified value.
For ThisLink = Each Link
   If Link.Value.Value = Value Then Return Link
Next



See the difference? Am I wrong here about the help file being ambiguous?


Dave:
Hey, not my fault the help documentation is telling me it does one thing, when it does another. :-) I was wrong about the compare thing, I admit it, but I still maintain that I was right about what the help file SAYS it does, versus what it ACTUALLY does.

And it doesn't help that the help file has no example code showing it in use either.


sswift(Posted 2006) [#10]
So is there any function which DOES find the link with the specified value?


Dreamora(Posted 2006) [#11]
Yeah:
function getLink:tlink (list:tlist, value:object)
local temp:tlink = list.firstlink()

while temp.value <> value
  temp = temp.nextlink()
wend
if temp.value = value
  return temp
endif
return false
end function



The question is, why you need the link at all. TList has all functionality to operate without touching its implementation as it should be in OO.
For fast linear access there is even .toArray()

And on your note above: Thats simply wrong.
It returns the first link as addfirst returns the link as well (insertafterlink / insertbeforelink returns a TLink as you clearly see in their implemenation)
But this assumes that your compare method is done correctly, otherwise it will return crap.

a > b -> a.compare(b) = 1
a = b -> a.compare(b) = 0
a < b -> a.compare(b) = -1

So your ordering has to handle that, how it does is up to you, but these 3 returns are what makes the list work or break. And sadly, your implementation simply sux in returning 0 which means "equal", so you will never see anything different than null! (if you do not have a compare in your type then it must even be reference pointer equal to get a compare = 0!)

Hope your "vamp hunt" on BM is soon over as you get used to how it works and not interpreting things into the source that are not there.
if there were bugs in the main modules like TList, we would have spoted them quite some time ago!


sswift(Posted 2006) [#12]

The question is, why you need the link at all. TList has all functionality to operate without touching its implementation as it should be in OO.



Let's say I do:

For ThisSprite = Eachin Sprite.SpriteList
Next

How, inside that loop, would you get the next sprite, and the previous sprite in the list?

(Note: Using ToArray, or a repeat loop is not an option here. I'm asking how you would do it from within that loop, not how you would implement it differently or better.)



And on your note above: Thats simply wrong.
It returns the first link as addfirst returns the link as well (insertafterlink / insertbeforelink returns a TLink as you clearly see in their implemenation)
But this assumes that your compare method is done correctly, otherwise it will return crap.




I didn't understand a thing you just said.

Addfirst returns a TLink. Correct.

But what you pass to it is a "Value". And that "Value" here is a pointer to a sprite.

FindLink's help says it finds the "Value" of a link.

But the "Value" of a link is the pointer to the sprite.

The Value of the Sprite is the sprite's Order, because of how I implemented my Compare() method.

What Findlink is actually finding is the Value of the Sprite, which is to say, the Value of object the the Value of the Link points to.



And sadly, your implementation simply sux in returning 0 which means "equal", so you will never see anything different than 0! (if you do not have a compare in your type then it must even be reference pointer equal to get a compare = 0!)




Once again, I can't understand a thing you're saying.

First of all, I'll have you know, I copied that implementation from one of the examples.

Method Compare(OtherObject:Object)

	Local OtherSprite:Sprite = Sprite(OtherObject)
	If OtherSprite = Null Then Return 0
						
	Return Order - OtherSprite.Order
			
End Method


Second of all, I know it's correct. Explain to me how the return value there can never be anything BUT 0?

Sprite 1 order - Sprite 2 order = Result
----------------------------------------
5 - 5 = 0 (EQUALS)
5 - 1 = 4 (GREATER THAN)
5 - 7 = -2 (LESS THAN)

Looks like it should work fine to me.



Hope your "vamp hunt" on BM is soon over as you get used to how it works and not interpreting things into the source that are not there.
if there were bugs in the main modules like TList, we would have spoted them quite some time ago!




When I posted, I didn't really think there was a bug, I did actually think I did something wrong.

But let me ask you something.

If the help file says a function does something, and the function doesn't do what the help file says it should do, does that mean the function is broken, or the help file is wrong?

In this case, I would say the help file is wrong. But really, you could look at it either way. That's why I kept insisting the function was broken. It does NOT behave the way the help file says it does.

Now that I know more about it, it's clear the function works the way it was INTENDED to work, but the help file just describes how it works wrong.

Btw, I don't think ANYONE here would argue that the HELP FILES are perfect, and that all "bugs" with THOSE have been found. And that is what caused this whole mess.

So yes, I admit, I used FindLink wrong. But I used it wrong because I used it how the help file said it works. The help file description needs to be changed.


Also:



Sswift:
So is there any function which DOES find the link with the specified value?

Dreamora:
Yeah:

function getLink:tlink (list:tlist, value:object)
local temp:tlink = list.firstlink()




"Yeah" is incorrect. I searched linkedlist.bmx, and there is no GetLink() function.

I didn't ask if it was possible to make such a function. I asked if there was one. In the language. In the help file. There isn't. I know you can make one yourself, but that's not what I was interested in. The whole point of trying to use it in the first place was becuase I was lazily trying to save time instead of coding a custom repeat loop for looping through the linked list. (Which is what I ended up doing.)


Btw, I think we're having a bit of trouble communicating here. So don't get too frustrated with me.


Dreamora(Posted 2006) [#13]
I'm sorry if I were a little harsh.
Its just that you have the answer in front of you and you still state that it is BMs fault.

As mentioned, the FindLink gives you the link (if ...compare = 0 return link). BUT this only happens if your type has a method compare that will return 0 if the types are "equal" according your definition of equal for this specific type.
The problem is your implementation. It only returns 0 if the other type is null which will never happen.

You should instead think about what "equalness" means in your list and set the return to reflect that.
If you can't think of another way than reference pointers, then my function above is the only way to go I fear. (I created the function so you do not need to hack the module and thus do not lose it on sync etc)
And no, this is not BMs fault but an OO rule that a compare functionality is used and not pointer references as they are not guaranteed to be the same in a managed environment. Normally that should not matter anyway. Even from the intuitive and "normal" point of view its clear, that objects are equal ie "reflect the same thing" if the have the same properties. Its just that you need to decide which properties make them unique and compare them :-)

For a sprite system, I would most likely use image, x,y,depth or even only image and depth for batched depthsorting.


sswift(Posted 2006) [#14]
"You should instead think about what "equalness" means in your list and set the return to reflect that."

I don't think you understand what it was I was trying to do. Because I now understamd what was going on, and if FindLink was doing what I THOUGHT it was doing, and what I WANTED it to do, then Compare() would never be called.

I was looking for a way to get the next and previous sprites in the list of sprites. If I want to do that, there's no reason you would "compare" them.

You have the right idea about what I am using compare for, but that compare has nothing to do with what I asking about in the first place.

I didn't want to get the previous and next sprites in the Z order. I just wanted to get the previous and next sprites in the list. I was misusing FindLink. If FindLink behaved how I thought it would, that compare method would never be called, cause it has nothing to do with figuring out which sprite is before a sprite, and which sprite is after it, in the list.

It might be possible to rig up the compare method to work that way, but I need that for sorting by z-order, and it seems crazy to waste the compare method on telling you which sprite comes before or after the current one in the list, since the list itself knows that.

This all came about because I was trying to get the next and previous sprites in a for eachin loop. Apparently there's no function built into the language which will tell me the previous and next sprites in that circumstance. If I don't use the for eachin loop though, it's not a problem. I was just being lazy. At least I understand what FindLink does now though.


Chris C(Posted 2006) [#15]
I usually have a mode value for compare thats a global, then change it depending what I'm wanting to do..

seems to work fine for me


tonyg(Posted 2006) [#16]
was looking for a way to get the next and previous sprites in the list of sprites


Don't you need to use prevlink and nextlink?
There are loads of posts with code on moving through lists using the findlink, prevlink, nextlink etc commands.


sswift(Posted 2006) [#17]
tonyg:
Yes, you can use those. And I did when I couldn't get the findlink method to work.

I was trying to do a For ThisSprite = EachIn Spritelist, and then using ThisSprite, find out the sprite before and after it in the list, since I didn't have access to the link pointer with this method. So I tried FindLink. But FindLink doesn't find the link with the specified value, it finds the link with a value that points to the specified value. So I was using it totally wrong.

It was a lazy thing to do anyway and really wasteful because I would have ended up iterating through the whole list for each sprite in the loop. But I was only doing a test so I didn't care.


dmaz(Posted 2006) [#18]
Yeah, I don't think you want to use findlink for any list processing unless you are looking for a specific item. It looks like you may found your answers but just in case here is an easy example for you and anyone else looking at this thead

Type ttest
	Field x:Int
End Type

list:TList = New TList

For i:Int = 1 To 10
	t:ttest = New ttest
	t.x = i
	list.AddLast(t)
Next

l:TLink = list.FirstLink()
While l
	t:ttest = ttest(l.value())
	Print "~ncurrent: " + t.x
	If l.PrevLink() Print "prev: " + ttest( l.PrevLink().value() ).x
	If l.NextLink() Print "next: " + ttest( l.NextLink().value() ).x
	l = l.NextLink()
Wend