About Monkey 2 › Forums › Monkey 2 Programming Help › Structs and foreach loops
This topic contains 11 replies, has 6 voices, and was last updated by
nerobot 1 year, 6 months ago.
-
AuthorPosts
-
June 24, 2017 at 4:39 pm #8891
I have a List<Vec2f> and I want to iterate it and change it’s contents, after a few minutes I realized that structs because are handled by value would not be mutated as referenced classes do.
If you have any workaround on this I would be interested to know.
June 24, 2017 at 8:14 pm #8893You may use a temporary pointer.
Local structPtr:myStruct Ptr
structPtr=varptr myStructInstance
structPtr->x=123Never tried it though..
If you want a non temporary pointer you’ll have to keep your structs alive.June 24, 2017 at 8:39 pm #8894Hmmm, couldn’t figure it out with List… here’s how I do it with Stacks, though.
Monkey1234567891011121314151617181920212223242526272829#Import "<std>"Using std..Struct MyStructField value := 0.0EndFunction Main()Local myList := New Stack< MyStruct >'Populates listFor Local n := 0 Until 20myList.Add( New MyStruct )Next'Set value, copy struct back into listFor Local n := 0 Until myList.LengthLocal s := myList[n]s.value = Rnd( 100.0 )myList[n] = sEnd'read valuesFor Local n := 0 Until myList.LengthPrint myList[n].valueEndEndJune 25, 2017 at 2:49 am #8896Ethernaut’s code changed to use a list of Vec2f:
Monkey1234567891011121314151617181920212223242526272829303132333435363738#Import "<std>"Using std..Function Main()Local list := New List< Vec2f >'Populates listFor Local n := 0 Until 20list.Add( New Vec2f )Next'' Set value, copy struct back into list'Local iterator := list.All()While Not iterator.AtEndLocal item := iterator.Currentitem.x = Rnd( 100.0 )iterator.Current = itemiterator.Bump()Wend'' read values'For Local item := Eachin listPrint item.xEndEndAssign new value:
Monkey123456789101112131415161718192021222324252627282930313233#Import "<std>"Using std..Function Main()Local list := New List< Vec2f >'Populates listFor Local n := 0 Until 20list.Add( New Vec2f )Next'' Set new value'Local iterator := list.All()While Not iterator.AtEnditerator.Current = New Vec2f( Rnd(100.0), Rnd(100.0) )iterator.Bump()Wend'' read values'For Local item := Eachin listPrint item.x + " : " + item.yEndEndJune 25, 2017 at 5:01 pm #8919Oh, I see!
By looking at Monkey’s List source code, my guessing is I see that the All() method might cooperate with the Eachin statement in the background. The All method however returns a new Iterator, not the existing (the essense of the elements) one.
I did this experiment and I see that I can’t mutate the element of the list directly, however I can mutate the local copy. Is this behavior valid or not?
Monkey1234567891011121314151617181920212223242526272829#Import "<std>"Using std..Struct MyStructField StructValue := 0Method To:String()Return "MyStruct: " + StructValueEndEndFunction Main()Local list := New List<MyStruct>For Local n := 0 Until 5Local s := New MyStructlist.Add(s)NextLocal item := list.FirstNode().Valueitem.StructValue = 1000Print(list.FirstNode().Value)Print(item)EndJune 25, 2017 at 10:04 pm #8920I did this experiment and I see that I can’t mutate the element of the list directly, however I can mutate the local copy. Is this behavior valid or not?
Yes, because structs are always passed ‘by value’ (ie: copied), so this line…
Local item := list.FirstNode().Value
…copies FirstNode().Value to item.
One way to deal with thinking about this stuff is to mentally replace ‘some struct type’ with, say, ‘int’ (also a struct type) in which case you’ve simply got a list of Ints, and hopefully it’s clear that…
Local item := list.FirstNode().Value
…will set item to a *copy* of FirstNode().Value as, like all struct types, ints are copied when passed to functions, returned from functions or assigned to variables.
June 26, 2017 at 12:34 am #8923List extension ‘ForEach’ & ‘ForEachBackwards’ for use with structs:
Monkey12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182#Import "<std>"Using std..Class List<T> ExtensionMethod ForEach:Void( func:T(item:T) )Local iterator := Self.All()While Not iterator.AtEnditerator.Current = func(iterator.Current)iterator.Bump()WendEndMethod ForEachBackwards:Void( func:T(item:T) )Local iterator := Self.Backwards()While Not iterator.AtEnditerator.Current = func(iterator.Current)iterator.Bump()WendEndEndFunction Main()'' Randomize Rnd values'Sleep( Rnd(1.0) )SeedRnd( Microsecs() )'' Create a linked list using Vec2f type'Local lst := New List< Vec2f >'' Add elememts to the list'For Local n := 0 Until 10lst.Add( New Vec2f )Next'' Set values'lst.ForEach( Lambda:Vec2f( element:Vec2f )Global count:Int = 0count += 1element.x = countelement.y = Rnd( 100.0 )Return elementEnd )'' print values'For Local item := Eachin lstPrint item.x + "~t" + item.yEndPrint "----------"'' Change all 'x' values in the list'lst.ForEachBackwards( Lambda:Vec2f( element:Vec2f )element.x += 100Return elementEnd )'' print values'For Local item := Eachin lstPrint item.x + "~t" + item.yEndEndJune 26, 2017 at 1:28 am #8929Nice!
September 25, 2017 at 2:39 pm #10803Another question is how can I send the struct to a function for updating?
This currently does not work:
Monkey12345678910111213141516171819202122232425262728293031323334#Import "<std>"Using std..Function Main()Local items := New List<Vec2f>For Local n := 0 Until 10items.Add(New Vec2f(Rnd(1,10), Rnd(1,10)))NextPrintItems(items)ResetItems(Varptr items)PrintItems(items)EndFunction ResetItems(items:List<Vec2f> Ptr)Local iter := items->All()While Not iter.AtEnditer.Current.X = 0iter.Current.Y = 0iter.Bump()EndEndFunction PrintItems(items:List<Vec2f>)Print("Items")For Local i := Eachin itemsPrint(i)NextEndSeptember 25, 2017 at 8:40 pm #10806Same problem – Current is returning a *copy* of the iterator item. Modifying this copy has no effect.
Actually, if you just use Vec2’s ‘x’ and ‘y’ fields directly (not ‘X’ and ‘Y’ properties), eg: iter.Current.x=0, you will get a compile time error, because the compiler ‘knows’ you are trying to modify a copy. However, the compiler doesn’t know exactly what X or Y properties do – they may or may not actually modify Self – and I didn’t want to just disallow property/method calls in general on returned copies or subject people to the joys of ‘const correctness’, so it’s a bit of a half-assed check.
The solution is to get current item, modify it, and then set it back (aka read-modify-write), eg:
Monkey123456789101112131415161718192021222324252627282930313233343536#Import "<std>"Using std..Function Main()Local items := New List<Vec2f>For Local n := 0 Until 10items.Add(New Vec2f(Rnd(1,10), Rnd(1,10)))NextPrintItems(items)ResetItems(Varptr items)PrintItems(items)EndFunction ResetItems(items:List<Vec2f> Ptr)Local iter := items->All()While Not iter.AtEndLocal v:=iter.Currentv.X=0v.Y=0iter.Current=viter.Bump()EndEndFunction PrintItems(items:List<Vec2f>)Print("Items")For Local i := Eachin itemsPrint(i)NextEndSame applies to Stacks etc.
However, you don’t need to do this with plain arrays, eg: myarray[i].X=0 will work fine, as an array element is really just a special type of variable (which is why you can Varptr it).
This means you can in fact do a little hack with stacks where you can set elements directly:
mystack.Data[i].X=0
This is because the Data property of Stack returns the underlying array. You do need to be careful ‘i’ is in range though, as array may be longer than Stack!
September 26, 2017 at 7:14 am #10814Oh, I see! So this assignment statement is really important from what it seems.
September 26, 2017 at 7:33 am #10815This means you can in fact do a little hack with stacks where you can set elements directly
Hm, very interesting!
-
AuthorPosts
You must be logged in to reply to this topic.