Why don't we have EACH?

BlitzMax Forums/BlitzMax Programming/Why don't we have EACH?

sswift(Posted 2006) [#1]
Sure, we have EachIn. EachIn iterates through a list. But EachIn returns the object that each link in the list points to.

Each would not take this extra step. It would simply return the next link in the list.

Why is EachIn bad?

If I am iterating through a list, and I want to remove an element from the list, with EachIn, I don't get a pointer to the current link. So I have no way to delete it directly.

The only option in such a loop is to use ListRemove. But listremove actually has to iterate through the list to find the link that points to the object in question. And what if you should have the same object in the list more than once? Then it will fail.

The only alternative to this is to use a While loop instead.

And let's face it, if we didn't care about having to go through the two extra steps we need to to set up and maintain a while loop, we wouldn't even have EACHIN.

Also, EachIn is fairly useless as is. I'm surprised it's stuck around this long. I mean how often to you make a list and then iterate through it and NOT need to delete elements in the list? Most lists I use are for dynamic objects, like particles, or enemies, or powerups, or bullets, or what have you, and all those things need a condition to check to see if it is time to free them from the list.


Chris C(Posted 2006) [#2]
FirstLink & NextLink seem to work fairly well for me...


R0B0T0(Posted 2006) [#3]
It's not the ideal solution, but if you generally only have the object in a single list, you could add a Link field in the type that gets updated when the object is added to the list (the tList Add methods return a link), and then use this direct handle for removal/insertion etc.


sswift(Posted 2006) [#4]
Compare:

Local ThisLink:TLink
Local ThisAnim:Anim

ThisLink = FirstLink(AnimList)
While ThisLink <> Null
   ThisAnim = Anim(ThisLink.Value())
   If ThisAnim.Age > 1000 Then ThisLink.Remove()
   ThisLink = ThisLink.NextLink(AnimList)
Wend



Local ThisLink:TLink
Local ThisAnim:Anim

For ThisLink = Each AnimList
   ThisAnim = Anim(ThisLink.Value())
   If ThisAnim.Age > 1000 Then ThisLink.Remove()
Next



Working fairly well is no excuse. We have For loops for numbers. We have for loops with EachIn. Both can be done with While looops if we don't mind incrementing the value ourselves. But we have the For loops anyway. And why? Because it makes our lives easier, and the code shorter and neater.


sswift(Posted 2006) [#5]
Roboto:
I know, I thought of that, but it's no better than using the crummy While loop really.


N(Posted 2006) [#6]
All of my objects store their links internally. That way, I can call MyObject.Dispose and it's removed from whatever list it's in on its own.


sswift(Posted 2006) [#7]
Much as I hate to do it, I might do that. I've just converted two functions to use while and it's just making a mess of things and making small functions twice as big.

On the other hand doing that requires me to store that pointer in every single function that can create an animation object and I have like ten of them. I could make one default create function to create an anim object which then has to be modified by said functions though, I suppose.

That seems like it might confuse the user though. They might think they need to call the create function.

Hm.


N(Posted 2006) [#8]
On the other hand doing that requires me to store that pointer in every single function that can create an animation object and I have like ten of them.


Do explain what you mean by this, because I'm afraid it sounds a lot like you're confused as heck.

<Stuff posted before I spotted your reply>
Here's an example:
SuperStrict

Type Foo
    Field _link:TLink
    
    Method Link( list:TList ) ' Convenience method
        If _link Then _link.Remove( )
        _link = Null
        If list Then _link = list.AddLast( Self )
    End Method
End Type

Local list:TList = New TList

For Local i:Int = 0 To 63
    New Foo.Link( list )
Next

For Local i:Foo = EachIn list
    i.Link( Null )
Next



sswift(Posted 2006) [#9]
I'm not confused, I know exactly what I'm doing. :-)

Here is what I've decided to do:

	' -------------------------------------------------------------------------------------------------------------------------------------------------------
	' This function creates a new Animate instance.  You should not need to call it.
	' -------------------------------------------------------------------------------------------------------------------------------------------------------
	
		Function Create:Animate()
		
			Local Anim:Animate 
			
			' Create a new Animate instance.
				Anim = New Animate
			
			' Add it to the end of the AnimList, and save a pointer to the link created.
				Anim.Link = AnimList.AddLast(Anim)
			
			' Return the pointer to the new Animate instance.		
				Return Anim
			
		End Function


	' -------------------------------------------------------------------------------------------------------------------------------------------------------
	' This function moves a BOB from one X position to another, over time.
	'
	' You may run both X and Y position animations simulataneously and with different parameters.  Keep in mind though that specifying a new PositionX() animation will only
	' override the X component of a previously applied Position() animation.  Same for PositionY().
	'
	' You can use PositionX() and PositionY() seperately with different settings to do advanced effects like text moving in a sine wave across the screen.
	' -------------------------------------------------------------------------------------------------------------------------------------------------------
		
		Function PositionX(ThisSprite:Sprite, StartX#, EndX#, Time, Flags=0, TriggerDelay=0, FreeSprite=False, StartAge=0)
		
			Local Anim:Animate
		
			FreeTriggered(ThisSprite, ANIM_POSITIONX)
		
			Anim = Animate.Create()
				
				Anim.Sprite       = ThisSprite
				Anim.Transform    = ANIM_POSITIONX
					
				Anim.StartX#      = StartX#
				Anim.EndX#        = EndX#
				
				Anim.Time         = Time
				Anim.Flags        = Flags
				Anim.Age          = -1
				Anim.TriggerDelay = TriggerDelay
				Anim.FreeSprite   = FreeSprite
				Anim.StartAge     = StartAge
				Anim.Speed#       = 1.0	
		
			Update()
					
		End Function



The user will call Animate.PositionX(Sprite, etc...) to start a new X position animation for a sprite. If I did not make that Create() function just now, then in the PositionX() function, I would have had to have:
Anim = New Animate
Anim.Link = AnimList.AddLast(Anim)

Which would have been just:
Anim = New Animate
AnimList.AddLast(Anim)

Except that I had forgotten to add the anim objects to the list, and it would not have required a third line as I thought it would to assign the new link to the type.

But anyway, the way I ended up doing it is cleaner. Now I don't even need that AnimList.AddLast(Anim) thing in each of those animation creation functions. I suppose I could even get rid of a whole MESS of default variable initialisations actually. Like um... Age = -1. Well maybe not that many. I guess I could change the create function to take some parameters though. Sprite, transform, time, flags, triggerdelaye, freesprite, startage, speed... All those could just be passed to the create function and I wouldn't have to assign them in every helper function.

Hmm.. I could even stick FreeTriggered in there. This will clean up the code significantly.

On second thought... I don't even want FreeTriggered there. That's a bug that has been in my AniBOB system I guess. Now that I think about it, that is not a good idea now that there is a trigger delay in the two systems. That means an animation playing currently would be shut off immediately if you add one to the queue, even if the new one doesn't trigger immediately. The update function should delete those triggered anims properly so this isn't even needed here. I think.


FlameDuck(Posted 2006) [#10]
Also, EachIn is fairly useless as is.
No it isn't. If you're upset with the way the ListItterator works, all you have to do is change the NextObject Method in TListEnum to return links instead of values.

Ofcourse the ideal solution would be to extend TList and override the ObjectEnumerator (which is actually an itterator, but don't tell anyone) Method with your own code.


sswift(Posted 2006) [#11]
So you're saying I could make SList Extends TList and then make an ObjectEnumerator method in that which returns the pointer to the next link in the list instead of the next object?

A clever idea, but not a very desireable solution. The idea here is to make everyone's life simpler and improve the language. Creating a special linked list type that I have to include in all my projects and code in some way that all the includes don't conflict with one another and making users use it if they want to modify the libs just doesn't seem like a good idea.

I'm sure you might suggest I make a lib or something out of it, but I say that using libs right now is a bad idea because of how the installer works, and how user libs get mixed in with the main ones. If you have to delete your max dir every time you install a new version, then you might accidentally delete your libs. Or if you give your program to someone you'll forget those files that are needed. And then there's all sorts of crap you'll have in there that is unneeded and it will be hard to sort through it all once it gets messy. So.... I don't like the idea of libs in max to make new commands. :-)


Also, I wish people would stop suggesting folks alter the default libs. Are you crazy? If people do that, they're bound to run stuff from other people which doesn't work, and then tell you there's a bug in your stuff when they really just screwed up their BlitzMax!


Wiebo(Posted 2006) [#12]
[offtopic] Welcome to blitzmax, the build-it-yourself language (and write-your-own-docs) environment. Before you know it, there will be branches of bmax, just like linux. welcome to development hell [/offtopic]


Dreamora(Posted 2006) [#13]
Naw ... that won't happen. But before you know there are 2 zillion extended types you could use so you won't find the correct one anymore ;)


FlameDuck(Posted 2006) [#14]
So you're saying I could make SList Extends TList and then make an ObjectEnumerator method in that which returns the pointer to the next link in the list instead of the next object?
Something like that.

Creating a special linked list type that I have to include in all my projects and code in some way that all the includes don't conflict with one another and making users use it if they want to modify the libs just doesn't seem like a good idea.
First of all, your users don't have to use it. That's the whole point of the Object Oriented Paradigm - you hide implementation from the users. And you shouldn't use includes, you should use import. You just want to use the interface, not replicate the code.

I'm sure you might suggest I make a lib or something out of it
Actually I would suggest nothing of the sort. I would simply import it into whichever sources I needed it for.

Also, I wish people would stop suggesting folks alter the default libs.
Who suggested that?

Welcome to blitzmax, the build-it-yourself language
I'm sorry it doesn't do what you want it to. I think you'll find the same thing with just about every programming language there is. With such a heterogeneous community, I seriously doubt you'll ever be able to design a language that does everything everyone wants, without it being rediculously complicated.


sswift(Posted 2006) [#15]

First of all, your users don't have to use it. That's the whole point of the Object Oriented Paradigm - you hide implementation from the users.



Except that on a large game project that uses one of my libs the chances that they'll need to modify my lib in some way are pretty good.


Actually I would suggest nothing of the sort. I would simply import it into whichever sources I needed it for.



What's the difference between import and include? I thought import was only for modules, but the help file indicates otherwise. Using Import and placing it in the modules directory, is what I meant by a lib.

I think I did a test with include once, and it did not do what the help file says it does... Include the source file at that location in the code. It didn't work in the middle of a for loop or in a function if I remember. So I think there's some subtle difference between the two.

Maybe Import allows you to import things in any order you want and they will work? I seem to recall in my Lego game I had to seperate my functions from my types in several libs which were dependent on one another, because I had to specify the types before the functions that called them. So I had to include the types for both libs before the functions that would use them.

Does Import avoid this issue? Does it do anything to protect your code at all above and beyond what include does? The help file example doesn't really help much here.

Perhaps locals in an include become acessable to the main program, while locals in an import do not?


[quote]
Also, I wish people would stop suggesting folks alter the default libs.


Who suggested that?
[/quote]

You:


all you have to do is change the NextObject Method in TListEnum to return links instead of values.



Other people have suggested making similar changes to the main modules when something didn't work the way I wanted. Like changing a private declaration to public. But it would be crazy to do that.


Dreamora(Posted 2006) [#16]
Import will compile the named bmx ie it will link the object files of the 2 bmx files instead of using its actual source. This has 2 advantages:

1. The imported BMX does only know of everything it imports. It has no ideas, what the later importing program variables are or what else exists there.

2. something that is imported is only recompiled, if it changed (if quick compile is enabled). This way you can save much time with larger projects.


sswift(Posted 2006) [#17]
Well I see your point on 2, but I don't get what the benefit of 1 is. With an include you get effectively the same thing, if I understand correctly. It doesn't really matter if the main program can "see" the code in the functions or not if it can't access them.

Can an imported file access globals in your main program? Perhaps that is the difference.

If I have a function in an include like so:

Function blah()
For Loop = 1 to 10
next
Next

And the main program has a Global called Loop, then with the Include, the global will be modified by the function, because the function will assume Loop is the global variable.

But maybe with imports that doesn't happen? How could it? The file is compiled seperately and ahead of time, so how could it know it will need to access a global?

That would mean you can't access globals in your main program from an import, but you can access them from an include. I'm not sure you'd want to do that, but that would be an important difference.


Dreamora(Posted 2006) [#18]
Correct, Imports can't access your main programs globals or anything that is in the import tree "above" them. They only know of the world they temself import and have.

On the why you would do that: This way, you can encapsulate code that does not need to have a reference to an outside point. For example your sprite system would be such a thing, where it makes sense to encapsulate it.

Include is most likely to seperate logical units and mostly needed in situations where BM fails due to its incomplete C like implementation (because you can't define types and implement them elsewhere, you often get in the situation where you create "import circles" that are not allowed. In that situation you include all the needed files into a new bmx and import that for example)

I normally wouldn't use Include anymore. It just makes it far harder to track all globals, types etc you have in the file as the file parts are split all over different files.


Modules, as you mention them, are again another thing. They exist on a "higher" level and can be used as framework for example. They can have included documentation information as well, that will lead to html documenation file on build modules.