About Monkey 2 › Forums › Monkey 2 Programming Help › Four different ways to iterate.
This topic contains 5 replies, has 4 voices, and was last updated by 
 Jesse
 2 years ago.
- 
		AuthorPosts
 - 
		
			
				
April 1, 2017 at 3:01 pm #7674
This is not a request for help, but rather a snippet. I don’t know if there is a better place to post this but anyway here it is. Having a class that acts as a container of a list – exactly as the standard List<T> but not a list implementation, more as a wrapper of a list – you can iterate it’s contents with four different ways. My favorite option is “D” as you see below because you can essentially trick the API and handle it virtual as a list.
Updated Code: 08 Apr 2017
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136#Import "<std>"Using std..Class Collection<T>' Option AField Items := New List<T>' Option BMethod ToArray:T[]()Return Items.ToArray()End' Option CMethod All:List<T>.Iterator()Return New List<T>.Iterator(Items, Items.FirstNode())End' Option DOperator To<C>:List<C>()Return ItemsEndEndGlobal Collection_:Collection<Int>Function GenerateCollection(quantity:Int)Collection_ = New Collection<Int>For Local i := 1 To quantityCollection_.Items.Add(Cast<Int>(Rnd(0, 100)))NextPrint("Created: " + Collection_.Items.Count() + " items.")EndFunction TestA()' A' print the items based on list iterationFor Local i := Eachin Collection_.Items'Print("A: " + i)NextEndFunction TestB()' B' print the items based on the array overload methodFor Local i := Eachin Collection_.ToArray()'Print("B: " + i)NextEndFunction TestC()' C' print the items based on iteratorFor Local i := Eachin Collection_'Print("C: " + i)NextEndFunction TestD()' D' print the items based on castFor Local i := Eachin Cast<List<Int>>(Collection_)'Print("D: " + i)NextEndClass Benchmark AbstractPrivateGlobal TimeStart:IntGlobal TimeEnd:IntGlobal TimeResult:IntPublicFunction Start()TimeStart = Millisecs()TimeEnd = 0EndFunction Stop()TimeEnd = Millisecs()TimeResult = TimeEnd - TimeStartEndFunction PrintResult()Print("Time Elapsed: " + TimeResult)EndEndFunction Main()GenerateCollection(1000000)Print("Starting Test A")Benchmark.Start()TestA()Benchmark.Stop()Benchmark.PrintResult()Print("Starting Test B")Benchmark.Start()TestB()Benchmark.Stop()Benchmark.PrintResult()Print("Starting Test C")Benchmark.Start()TestC()Benchmark.Stop()Benchmark.PrintResult()Print("Starting Test D")Benchmark.Start()TestD()Benchmark.Stop()Benchmark.PrintResult()EndApril 8, 2017 at 3:27 am #7774Does any one way have a speed advantage over the others?
April 8, 2017 at 10:06 am #7783I did a measurement to see for real, time is in millisecs.
Monkey123456789Created: 1000000 items.Starting Test ATime Elapsed: 160Starting Test BTime Elapsed: 64Starting Test CTime Elapsed: 158Starting Test DTime Elapsed: 157Some back story behind this test.
In real circumstances someone would go for the option A which is the most obvious and well understood concept. So this means that it’s end of story.____ All of the other ways are most close to the concept of internal DSLs (I learnt about it these days) it’s about how you can utilize these techniques to create a very smooth API. ____ Option B converts your list to array, Option D proves how Monkey can use the To operator effectively to convert from List to List<B>. However option C is my favorite because it provides this smooth API transition, this way you can bypass internal class contents and let the object present itself according to how it is called (To operator in action).
Regarding the benchmarks
One striking thing to note in these benchmarks is that I never expected that array iteration (B) would be this fast. I had a subconscious fear for this due to this array conversion, (from the point I have a list ready to use – I see no reason to convert it to a list and iterate it) – however as it seems there is some compiler logic that make this happen. As far as I see this conversion happens only once, then the compiler will have to jump straight into array blocks. On the other hand option (A) as it involves objects and pointers, makes accessing the memory much more complicated.April 8, 2017 at 11:42 am #7784here! test E is way faster.
[/crayon]Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154[crayon-5cba8c036e315265052391 inline="true" ]Using std..Class Collection<T>' Option AField Items := New List<T>' Option BMethod ToArray:T[]()Return Items.ToArray()End' Option CMethod All:List<T>.Iterator()Return New List<T>.Iterator(Items, Items.FirstNode())End' Option DOperator To<C>:List<C>()Return ItemsEnd'Option EMethod ToNode:List<T>.Node()Return Items.FirstNode()End MethodEndGlobal Collection_:Collection<Int>Function GenerateCollection(quantity:Int)Collection_ = New Collection<Int>For Local i := 1 To quantityCollection_.Items.Add(Cast<Int>(Rnd(0, 100)))NextPrint("Created: " + Collection_.Items.Count() + " items.")EndFunction TestA()' A' print the items based on list iterationFor Local i := Eachin Collection_.Items'Print("A: " + i)NextEndFunction TestB()' B' print the items based on the array overload methodFor Local i := Eachin Collection_.ToArray()'Print("B: " + i)NextEndFunction TestC()' C' print the items based on iteratorFor Local i := Eachin Collection_'Print("C: " + i)NextEndFunction TestD()' D' print the items based on castFor Local i := Eachin Cast<List<Int>>(Collection_)'Print("D: " + i)NextEndFunction TestE()'E'Print the Items based on NodeLocal node := Collection_.ToNode()While node.Value'Print("E: " + i.Value)node = node.SuccWendEnd FunctionClass Benchmark AbstractPrivateGlobal TimeStart:IntGlobal TimeEnd:IntGlobal TimeResult:IntPublicFunction Start()TimeStart = Millisecs()TimeEnd = 0EndFunction Stop()TimeEnd = Millisecs()TimeResult = TimeEnd - TimeStartEndFunction PrintResult()Print("Time Elapsed: " + TimeResult)EndEndFunction Main()Print "Processing ....."GenerateCollection(10000000)Print("Starting Test A")Benchmark.Start()TestA()Benchmark.Stop()Benchmark.PrintResult()Print("Starting Test B")Benchmark.Start()TestB()Benchmark.Stop()Benchmark.PrintResult()Print("Starting Test C")Benchmark.Start()TestC()Benchmark.Stop()Benchmark.PrintResult()Print("Starting Test D")Benchmark.Start()TestD()Benchmark.Stop()Benchmark.PrintResult()Print("Start Test E")Benchmark.Start()TestE()Benchmark.Stop()Benchmark.PrintResult()EndApril 9, 2017 at 12:31 am #7801Your TestE function is wrong. Hint: it has something to do with your while condition.
EDIT: Okay here’s a solution (because Lists in MX2 are apparently circular):
[/crayon]Monkey1234567891011[crayon-5cba8c03746b4058894228 inline="true" ]Function TestE()'ELocal firstNode := Collection_.ToNode()Local node := firstNodeRepeat'Do something useful here.node = node.SuccUntil node = firstNodeEnd FunctionApril 9, 2017 at 1:50 am #7803@impixi you are right. hahaha! At first I didn’t know why it wasn’t working. That’s because I have always used it with objects not integers or floats so I just assumed it was doing what it was supposed to. it works fine with objects not primitives. your solution solves it. I have to keep that in mind. unlikely I will use integers or floats in a list but who knows. Thanks!
It is still faster but not as I gave it credit for.
 - 
		AuthorPosts
 
You must be logged in to reply to this topic.