How to keep track of EachIn loop counter?

BlitzMax Forums/BlitzMax Programming/How to keep track of EachIn loop counter?

When I use EachIn for a loop counter, is there some way to access where in the array/list the loop currently is?

ie

Local myarray:mytype[50]
Local counter:mytype
For counter=EachIn myarray
   Print counter
Next

Should this display indexes in the array (0..49) or should it display pointers to each instance of the type?

If you use EachIn for the looping, how can you know what position you are currently at in the array?

That code won't work at all.

The easiest way is to have a field that you set to the index of the array when you create each object.

If you need to keep track of the current index of an array then why use EachIn?

Type mytype
	Field fred
End type

Local myarray:mytype[50]

For counter = 0 Until myarray.length
	myarray[counter] = New mytype
	myarray[counter].fred = counter
Next 

For counter = 0 Until myarray.length
	Print counter + " = " + myarray[counter].fred 
Next
Or have I got the wrong end of the stick?

Yes, you could do it that way, but if you have a large (but sparse) array then my suggestion would be faster.

Cus EachIn is faster.

.

If you need to keep track of the current index of an array then why use EachIn?
A even better question might be "Why do you need to know the index at all"? Does it matter?

Why not just use a var?
Local myarray:mytype[50]
Local counter:mytype
Local index:Int=0

For counter=EachIn myarray
  index:+1
  Print index+": whatever"
Next


Neville:
For counter = 0 Until myarray.length
Should be:
For counter = 0 Until myarray.length-1

Arrays are 0 based remember :)


Tom

Until doesn't process the last element remember :)

owned! :)

Is EachIn the fastest way to step through an array?

I thought it was there more for convenience than speed and Blitz was just stepping through the array and returning any non NULL values [edit for clarity]to save you having to do it manually[/edit].

How are arrays stored then?


Sorry if these seem like daft question. I'm just curious.


Oh...;op @ Tom ;o)

Squatting - my mistake, I was under the impression that arrays were cleverer or that I was using Blitz3D! :P

This works:
Type mytype
	Field x,y
End Type

Local myarray:mytype[50]

For counter = 0 Until myarray.length
	If Rand(1,5) = 1 Then  	
		myarray[counter] = New mytype
	EndIf
Next 

For counter = 0 Until myarray.length
	If myarray[counter]
		Print counter + " = " + counter 
	EndIf
Next


I don't know results overall but in my case I found that changing from a For counter=0 to whatever loop, to a For mytype=EachIn myarray loop, gave about a 12-15% speedup.

It is my *guess* that if you access an array individually for each iteration of the loop, using an index counter as the loop counter (or with a separate variable), each time you access the array it has to take the base address of the array, multiply (or shift) the size of the array type by the index variable, and add it to the base, before it can read the actual content. With the EachIn command, it *probably* can bypass a lot of that be just setting up the base address first and then, for every loop, add the size of the type to it. Much easier and quicker. That probably gives a speedup. Not sure how it works for linked lists though.

I just was wondering if there was some way that you could know where the EachIn had got to in the array. There are useful reasons to be able to react to the loop counter. Obviously you can use a loopcounter as in For counter= bla bla bla, but then you can't use EachIn and you lose the speed gain.

What do you mean I can't eat my own cake?

I did some timings, and didn't see the percentages you saw.

I tried the .length method directly in the for/until statement, copying it to a local var and using the local var in the for statement and also using the For/EachIn.

Try this and see how you go (will only work on Win32, as I used the RDTSC, via QueryPerformanceCounter).

A useful piece of code in there is the TCodeTimer class, which gives you an accurate nano-second timer (dependant on CPU speed). I saw some of you asking for a better than millisecond timer, so I thought I'd provide this.

Framework brl.retro

' initialize the timer internals
TCodeTimer.Initialise()

Print "Timing 1..."

Local myarray:Int[1000]
Local dummy:Int

timer:TCodeTimer = TCodeTimer.Create()

timer.StartWatch()

While timer.WatchTime() < 1000

	timer.StartTimer()
	
	Local ma_len:Int = myarray.length - 1
	For counter = 0 To ma_len
		dummy = myarray[counter]
	Next 
	
	timer.StopTimer()

Wend
	
Print "Using length in local variable"
Print "Iterations: " + timer._iterations
Print "Total Time: " + timer._totaltime
Print "Average time: " + timer._totaltime / Double(timer._iterations)
Print "***************************"
Print 

timer.ResetTimer()

Print "Timing 2..."

timer.StartWatch()

While timer.WatchTime() < 1000

	timer.StartTimer()
	
	For counter = 0 Until myarray.length
		dummy = myarray[counter]
	Next 
	
	timer.StopTimer()

Wend
	
Print "Using length in For/Until"
Print "Iterations: " + timer._iterations
Print "Total Time: " + timer._totaltime
Print "Average time: " + timer._totaltime / Double(timer._iterations)
Print "***************************"
Print 

timer.ResetTimer()

Print "Timing 3..."

timer.StartWatch()

While timer.WatchTime() < 1000

	timer.StartTimer()
	
	For value=EachIn myarray
	Next 
	
	timer.StopTimer()

Wend
	
Print "Using length in For/Until"
Print "Iterations: " + timer._iterations
Print "Total Time: " + timer._totaltime
Print "Average time: " + timer._totaltime / Double(timer._iterations)
Print "***************************"
Print 



' classes
Extern "win32"
	Function QueryPerformanceCounter(count:Long Var)
	Function QueryPerformanceFrequency(freq:Long Var)
End Extern

Type TCodeTimer
	Field _start:Long
	Field _stop:Long
	Field _iterations:Int
	Field _totaltime:Double
	
	Method ResetTimer()
		_start 		= 0
		_stop 		= 0
		_iterations 	= 0
		_totaltime 	= 0
	End Method	
	
	Method StartTimer()
		QueryPerformanceCounter(_start)
	End Method
	
	
	Method StopTimer()
		QueryPerformanceCounter(_stop) 
		_totaltime :+ (Double(_stop - _start) / Double(_freq)) 
		_iterations :+ 1
	End Method
	
	
	''' Stopwatch functions
	Field _watchstart:Double
	
	Method StartWatch()
		self._watchstart = QueryCounter()
	End Method
	
	''' returns the number of milliseconds since calling StartWatch()
	Method WatchTime:Double()
		Return QueryCounter() - _watchstart
	End Method 
	
	''' Globals
	
	''' Initialize should be called before first use, to determine timer class overhead
	Global _overhead:Double	
	Global _freq:Long
	
	Function Create:TCodeTimer()
		Return New TCodeTimer
	End Function
	
	Function Initialise()
		QueryPerformanceFrequency(_freq)
		_freq :/ 1000
	End Function
	
	Function QueryCounter:Double()
		Local counter:Long
		QueryPerformanceCounter(counter)
		Return Double(counter) / _freq
	End Function 

End Type