Defining Functions

Started by Calin Leafshade, Wed 25/11/2009 23:47:48

Previous topic - Next topic

Calin Leafshade

So, I have two functions which call each other a couple of times.

the problem is that functions need to be defined higher in the script than the place they are called.

Obviously the two scripts cant be higher than each other so is there a way to define the functions in the general defs like a global script variable?

Calin Leafshade

Actually i've just realised that this is impossible.. you obviously cant call a function from within function which is within the same function...


Crimson Wizard

Unless I missed something, I think there's a problem in how AGS considers function definitions... in classic laguages you could make a function prototype and the function becomes known to every other module that includes header with definitions. In AGS, however, "declaring" a function as import does not work same way, it still oblidges to define function body before any actual call.

Crimson Wizard

Quote from: Calin Leafshade on Thu 26/11/2009 00:24:04
Actually i've just realised that this is impossible.. you obviously cant call a function from within function which is within the same function...

It is called recursion :) and it is possible in most programming languages.

GarageGothic

#4
I'm pretty sure you can in fact make a function call itself in AGS - at least I did it at one point, I think it was a flood fill algorithm, and it worked.  So if possible (e.g. if the two functions don't have too many parameters combined), you could in theory put all the code into one function and make it call itself with some parameter to decide which of the two sub-functions to run.

Edit: Tested it, and it does work, but it is possible to overload the call stack causing the game to crash, so be careful with how many recursive calls you make (which, I now remember, is why I rescripted my flood fill function to use a dynamic array as a stack instead of using recursion).

monkey0506

IIRC the maximum recursion level in AGS is 14 calls (I could be mistaken on this though). Beyond that it will overload the stack as GG says.

One of the most commonly referenced examples of recursion in programming is the Ackermann function which in AGS would look something like:

Code: ags
function ack(int m, int n) {
  if (m == 0) return n + 1;
  if (n == 0) return ack(m - 1, 1);
  return ack(m - 1, ack(m, n - 1));
}


So for:

Code: ags
ack(0, 0);
ack(1, 0);
ack(2, 0);
ack(3, 0);


You would get the results of: 1, 2, 3, 4.

For:

Code: ags
ack(0, 0);
ack(0, 1);
ack(0, 2);
ack(0, 3);


You would get these results: 1, 2, 3, 5.

And then for:

Code: ags
ack(1, 1);
ack(2, 2);
ack(3, 3);


You would get: 3, 7, 61.

Ryan Timothy B

I'm having a hard time understanding this exactly.. I didn't know a function's return could be used in this fashion.

I thought calling return would return the value it was told to return, and stop the function in it's tracks from that moment on.

Like for example:
Code: ags

function odd(int i)
{
  if (i%2==1) return true;
  //I thought it wouldn't run anything past this, IF the return was called.
}


Calling like this:
Code: ags
if (odd(21)) //etc


Wouldn't that stop the function the moment return was called, and return true (since 21 is odd)?  Perhaps I've been understanding the return command incorrectly. 
Put me back on the right track here. :P

RickJ

Return
The return statement works the way you describe.  However, it is usually considered good programming practice for function to have one entry point and one exit point.  The only time I consider using conditional return statements is for trivial functions containing 2 or 3 lines of code like this:   
Code: ags

function is_number(char c) {
     if ((c>='0')&&(c<='9')) return true;
     else return false;
}


Your example doesn't show what what happens when the "if" condition is false.  If that's all the code in your function then it is returning whatever junk happens to be in memory.

Recursion
You probably don't need top be doing this.   Recursive algorithms are typically used to walk hierarchical structures etc.

Ryan Timothy B

I use return statements often (although I forgot to post the 'return false' in my segment of code).  I'm very familiar with the way they work, I just got super confused with Monkey's post.  I just couldn't wrap my head around how, ack(3, 3)  would result in 61.  But now that I've taken a fresh look at it (I just took a nap, lol), I understand.

I didn't notice his last line which returns a function call, and a function call within that function call.  Pretty much a huge tangle of function calls (until m results in zero).  Now it seems pretty obvious to me, and I feel silly for being confused earlier.  Thanks anyway. :P

monkey0506

#9
In case anybody else was confused:
ack(3, 3)
ack(2, ack(3, 2))
ack(2, ack(2, ack(3, 1)))
ack(2, ack(2, ack(2, ack(3, 0))))
ack(2, ack(2, ack(2, ack(2, 1))))
ack(2, ack(2, ack(2, ack(1, ack(2, 0)))))
ack(2, ack(2, ack(2, ack(1, ack(1, 1)))))
ack(2, ack(2, ack(2, ack(1, ack(0, ack(1, 0))))))
ack(2, ack(2, ack(2, ack(1, ack(0, ack(0, 1))))))
ack(2, ack(2, ack(2, ack(1, ack(0, 2)))))
ack(2, ack(2, ack(2, ack(1, 3))))
ack(2, ack(2, ack(2, ack(0, ack(1, 2)))))
ack(2, ack(2, ack(2, ack(0, ack(0, ack(1, 1))))))
ack(2, ack(2, ack(2, ack(0, ack(0, ack(0, ack(1, 0)))))))
ack(2, ack(2, ack(2, ack(0, ack(0, ack(0, ack(0, 1)))))))
ack(2, ack(2, ack(2, ack(0, ack(0, ack(0, 2))))))
ack(2, ack(2, ack(2, ack(0, ack(0, 3)))))
ack(2, ack(2, ack(2, ack(0, 4))))
ack(2, ack(2, ack(2, 5)))
ack(2, ack(2, ack(1, ack(2, 4))))
ack(2, ack(2, ack(1, ack(1, ack(2, 3)))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(2, 2))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(2, 1)))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(1, ack(2, 0))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(1, ack(1, 1))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(1, ack(0, ack(1, 0)))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(1, ack(0, ack(0, 1)))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(1, ack(0, 2))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(1, 3)))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(0, ack(1, 2))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(0, ack(0, ack(1, 1)))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(0, ack(0, ack(0, ack(1, 0))))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(0, ack(0, ack(0, ack(0, 1))))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(0, ack(0, ack(0, 2)))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(0, ack(0, 3))))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, ack(0, 4)))))))
ack(2, ack(2, ack(1, ack(1, ack(1, ack(1, 5))))))
...
And that's quite enough for now. :P

DoorKnobHandle

#10
Just for reference, one of the main application of recursion (ie. functions calling themselves recursively) in game development is when you want to traverse some sort of tree. Imagine you want to import, let's say, a 3-d model into your game. That model includes a skeleton-like bone system for animation. The first node in that tree is the head, it has a child node below called neck, 3 child nodes for that called left shoulder, right shoulder and chest, those have child bones etc. all the way to the feet. Now, when you want to traverse this tree (this means: loop through all nodes, to initialize them or update their position for example, or whatever), the easiest way to do this is recursively. Get the first node (often referred to as root node as well), in our case the head, do whatever you want to do to it (initialize, update position etc.), and then loop through all the children of that node and call the same function. That's it, this will run your function on every node. Pseudo-code looks like this:

Code: ags

function UpdateNodePositionsRecursively ( Node *node )
// updates the position of a node recursively for all nodes
{
      // do whatever you want to do
      node->UpdatePosition ( );
      // ...

      for ( int i = 0; i < node->GetChildCount ( ); i++ )
      // loop through all the children of this node
            // call this same function for all children - THIS IS RECURSION
            UpdateNodePositionsRecursively ( node->GetChild ( i ) );
}

// and now when you want to loop through all nodes and
// update their position, all you need to call is...
UpdateNodePositionsRecursively ( model->GetRootNode ( ) );
// our function on the first node


Okay this was a little ex-course and probably half-offtopic but maybe this makes the practical application of the theoretical idea of recursion a little more clear or whatever, hope it helps. :D

Crimson Wizard

Erm, all of this is terribly interesting, but aren't you abusing Calin's thread?  ::)

The initially stated problem was that in AGS you cannot both call func1 from func2 and func2 from func1.

Calin Leafshade

Yeah! Losers.

Although i have kinda solved my problem by placing a while loop within one of the functions to rerun it when necessary.

The point really was that you cant have the following

Code: ags

function A(){

B();
}

function B(){

A();
}


obviously that would result in infinite recursion but add a condition in there and thats solved.

but function A cant 'see' function B because it's lower in the script.

I wondered if its possible to define the function template at the top of the script somehow.

monkey0506

#13
Calin's post beated mine. But Leafshade you could do something like this:

Code: ags
enum WhichFunction {
  eFunctionA,
  eFunctionB
};

function SomeFunction(WhichFunction which, int data) {
  if (which == eFunctionA) {
    if (data > 5) return SomeFunction(eFunctionB, data);
    else return FloatToInt(IntToFloat(data) * Maths.Sqrt(IntToFloat(data));
  }
  else if (which == eFunctionB) {
    if (data <= 5) return SomeFunction(eFunctionA, data);
    else return FloatToInt((IntToFloat(data) * 2.0) / (Maths.Sqrt(IntToFloat(data) + (IntToFloat(data) / 2.0));
  }
}


No I don't have any idea what such an absurd algorithm would be used for. I just made it up as I was going along. It's just a proof of concept. :P

As you said, you would need a conditional to prevent infinite recursion (overflowing AGS's call stack and crashing the game), but you could use a single recursive function with a parameter to define which functionality to use to produce the same effect you were looking for. ;)

Calin Leafshade

Quote from: monkey_05_06 on Thu 26/11/2009 15:06:54
No I don't have any idea what such an absurd algorithm would be used for. I just made it up as I was going along. It's just a proof of concept. :P

Its used for calculating awesomeness in terms of its prime factors. Congratulations.

SMF spam blocked by CleanTalk