ok I'm treading into some unfamiliar ground, so try to follow along
I'm trying to figure out how I could call a function recursively in MMF, through whatever trickery. I'll use a call to a fastloop to signify the function. So lets say our code looks like this
*On Loop "SimpleFunction"
=Do Stuff
Now, if we added that to our code, the way MMF seems to work is that as soon as an event calls a fast loop, it is immediately added onto the stack at that exact location, as if the code was copy/pasted in directly below the previous line that many times.
So if I have code that looks like this:
*Start of Level
=Do Nothing
*Always
=Start Loop "SimpleFunction" 2 times
*Start of Level
=End The Game
*On Loop "SimpleFunction"
=Do Stuff
Then in runtime, when the editor compiles it and adds it to the stack, it would go in this order:
*Start of Level
=Do Nothing
*Always
=LOOPCALLED:
=Do Stuff
=Do Stuff
*Start of Level
=End The Game
That seems simple enough, thats how fast loops work; they are added immediately on the stack, not having to wait for the code to travel all the way down to reach them first, but being transplanted a little north. Now my issue comes, in what order do loops travel on the stack, if they call THEMSELVES or OTHER LOOPS THAT CALL THEM, which is where it gets complicated.
Now this is a little intriguing, isn't it? I'm trying to figure out what happens in a scenario like this, and hoping someone knows the code structure well enough to tell me. In a C++ or essentially every other language that allows recursive function calls, the way it works is that code would add to the stack like this:
A
-B
--C
---D
----E
----X
---X
--X
-X
X
- none of the calls to the function get past the recursive call step until the last call to it executes; the code we had would look like this when compiled onto the stack:
STACK:
Add 1 to counter
Add 1 to counter
Add 1 to counter
Add 1 to counter
Add 1 to counter
Do Stuff
Stop Loop
Do Stuff
Stop Loop
Do Stuff
Stop Loop
Do Stuff
Stop Loop
Do Stuff
Stop Loop
Now herein, lies my question: Is this what happens with MMF? Or would each call to the same function execute in its entirety before proceeding to the next iteration? If this is so, recursion wouldn't be easy; you wouldn't be able to have code that is called AFTER the recursive calls have resolved, like most programmers do. You'd have to make sure the stack frames didn't overlap at all, and that the iterations went sequentially without ever having to backtrack to a previous frame to resolve. That takes alot of power and funky tricks away from the programmers.
Or would the recursive call even bring up a new copy of the function? Or would it be that calling a function from inside of itself would just lead the current loop to repeat an additional time instead of opening a "new" loop.
Even once you get past that, theres the issue; what happens when it reaches the "Stop Loop" code? Would a call from a iteration 10 deep call to the function terminate ALL of the open loops, or just the one that is currently being resolved by the compiler? If so, the loop wouldn't backtrack again in the example code, it would simply terminate after "Doing Stuff" once, instead of 5 times.
Recursion is proving to be a tricky subject, considering the unorthodox nature of the klik code, and I'm going to do more further testing in it to see how possible it is. Things like pathfinding, enemy AI's, some graphic functions, can be greatly simplified through recursion, but this is proving difficult in MMF compared to C
you have to understand that a fast loop != a function.
im not sure how the fastloop works in mmf so im not entirely sure you are right about adding the code to the stack.
in a c++ program a loop is done through iteration either through a while loop, do while loop, or for loop.
all of these do not produce more code, but merely loop back to the initial line of code the loop started on until certain requirements are met within and then stops.
as for recursion, i have yet to learn that as it is my next semesters topic in c++.
im guessing it means calling the same function from within that function itself?
in that case i do not think it is possible to do with fastloops. nor do i understand how that would be possible at all. wouldnt it create an infinitely deep loop?
Yes recursive programs often infinitely loop if you mess them up, thats a big pitfall. I'm not sure you understand how the stack works for computing though; the compiler reads through the MMF (or C, or any other languages) code from top to bottom, translating it into machine code; it is then written into each from of the stack in this order.
When you encounter a loop, in say C, it takes the looped coded and repeatedly writes it into the stack after each call (well in actuality using a JMP in the machine code, but its easier to think of it that way on the stack). So while the stack doesn't actually go and write out the code copy/pasting it a bunch of times, what does is exactly equivalent to that; a loop of:
//Psuedocode
While (5 loops){
Add 1 to counter;
}
is exactly the same result as
//Psuedocode
Add 1 to counter;
Add 1 to counter;
Add 1 to counter;
Add 1 to counter;
Add 1 to counter;
But you're exactly right; fast loops aren't functions. MMF is NOT a functional language, which makes trying to write code that is equivalent to a function a messy business. However, for normal usage, calling a fast loop is essentially the same as calling a function; it will add the loop's conditions/events into the MMF editor directly where you called the loop, resolve them, and then return to the editor. If you call a different loop from inside that loop, it will resolve the SECOND loop, then return to the first loop, and resolve it.
So if you use multiple copies of the same loop, its exactly the same as recursion, to that many degrees. I used that to great ends in my pathfinding engine I just posted, if you look at that code; I simply copy/paste the code of "Loop1", and instead of calling itself where it should recurse, it calls "Loop2", which has had its loop calls changed likewise, and so on.
The problem is that this obvious is a poor workaround, since you have to manually copy/paste a ton of code. Its like trying to work around a lack of looping by copy/pasting the individual events; inefficient and underpowered, and not capable of the same things.
So while I know a loop will properly call another loop and resolve in the exact order that a similar set of functions would execute in C if called from each other, I don't know exactly what happens when a loop calls ITSELF in MMF. But given the way that a loop call is parallel to a function call, I'm hoping to find a way to grind it up the stack and back down again and resolve like a bunch of recursive calls, instead of just halting on the first "Stop Loop", or grinding each stack in its entirety before proceeding to the next one.
i understand heap and stack, i just never realized when using the stack, a loop writes the machine equivalent that many times.
i was about to say. why not make a simple recursive loop that calls itself and see what happens. the worse that could happen is you overflow the stack and it crashes
btw is there anyway to increase the amount of memory used for the heap and stack? couldnt that theoretically speed up games/ allow for more objects etc, larger arrays? i said it in another post that the array object warned me that a 10,000x10,000 dim numerical array would use over 256gb of memory, which is bullshit. cause ive used a 10,000x10,000 array in c++ before
it depends on your array type. If its a list array, or whatever its called, the one with a keytable/cache or whatever, all it does is assign a key to each item you put into it; so even if you had 10000x10000, it would only have as much memory used as elements you define in it, all the elements you don't define wouldn't require any memory. If its a normal array, like MMF uses, all the elements are declared, so a 10000x10000 array would need to define 100,000,000 elements, and if each one took up 2kb of memory, thats over 256gb needed.
But no, loops don't write machine code over again generally speaking. Once you get into learning the machine code part, what it does is write a JMP line in the machine code that jumps to a higher point if a previous condition with one of the buffers is true/false, or whatever. But in essense its functionally equivalent to writing out the code multiple times on its back.
I designed a neat little test that showed some nice results:
This will run a neat little recursive function. It will run a counter both up and down, and terminate once it reaches 10.
Theres 3 hypothesises I'm going to test:
A) MMF can recurse with fast loops just fine:
In this case, it will add 1 to the counter, get to the "Start Fast Loop", then start the next one. On the 10th iteration, it will proceed past that step, setting the other counter to 1, then it will subtract 1, move down a stack frame, subtract 1 again, etc, until all the frames are resolved.
In this case, the results should be 0 & 1
B) Stack will resolve entirely before moving to next loop:
If this is true, the function will add 1, then subtract 1, then start over, and since the counter will always be 1 at the test for <10, it will loop infinitely, and crash.
In this case, the results should be a painful crash
C) MMF will recurse fine, but calling "Stop Loop" stops ALL copies of the loop, not just the active stack frame:
If this is true, it will proceed just like A) up to the 10th stack frame, then set the other counter to 1, subtract 1 from the main counter, then everything will terminate, and the function is finished.
In this case, the results should be 9 & 1
My test yielded this result:
Bam; MMF *CAN* recurse, and can do so exactly like a function in C++.
Now there might be some bad interactions between parallel running loops, like if I called Fun 4 times from inside itself instead of just 1, for example the stop loop might stop all parallel loops, but I'm too tired to test that right now. Who knows what else might be wrong.
Further testing also revealed a stack frame limit; setting the "<10" counter to say "<10000" made it crash, and after narrowing it down, I found that 8252 is the magic number. So on the 8252nd active fast loop, MMF will crash. I don't know if this only applies to multiple copies of a single loop, or total number of all loops active at a time, or total number of all loops used thus far in a frame. But trying to make more than 8252 recursive calls is bad business.
It is nice to know that it does work like that in MMF, however you should've just made a test application in the first place Also you could copy & paste that mumbo jumbo at the top about fastloops and make an article about it
Hey, just want to add something. I used loops (or functions, whatever you want to call them, since MMF loops are just functions you call a certain amount of times ) recursively a long time ago. It's been a while, so I don't remember everything and if I'd look in my game again, I probably won't understand it anymore. (should have documented it properly >_< )
I didn't read through all your posts thoroughly, just skimmed through it, but I noticed you don't use Loopindex("") to get the index of a loop (you use a counter). From what I can remember, it's important to remember that Loopindex("") doesn't reset itself whenever you call a loop/function again. That or Loopindex("") had a different behaviour, sorry I forgot It was something I was cracking my head over when I was using loops recursively. Something to investigate (properly) perhaps
Is there really a good use for recursive functions in an imperative language? (oh well, I guess there could be if you want to program multi-core) I guess you just love coding, Pixelthief.
I always thought loop index gave the current # of repetition of the same loop from a call, like it would start at 0 every time you called a loop, even if it was inside itself. If not it doesn't really matter since you can get either number from just adding / reseting a counter, but still.
of course that just simulates recursion by calling copies of a loop, but now that I know it works it could be adjusted a tiny bit to make it fully recursive!