Chapter 7

From SphereWiki
Revision as of 22:34, 1 June 2009 by MrSugarCube (talk | contribs) (Created page with ''''''(WIP)''''' ==Recursive Functions== I discovered this very seldom explored extension of SPHERE scripting while reading messages on the boards. Someone was trying to create a ...')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

(WIP)

Recursive Functions

I discovered this very seldom explored extension of SPHERE scripting while reading messages on the boards. Someone was trying to create a function that counted the number of items in a container using this sort of thing, and it worked for the most part. I was very amazed, because before that, no one had even thought of using functions that looped back upon themselves.


Which is what a recursive function is. I'll say it one more time.


A recursive function is one that calls itself, or recurs. Surprise!


So how do we do this? It's as simple as calling a function:


[FUNCTION recursive_test]
recursive_test


This very small piece of code in fact IS a recursive function. As you can see, the function will call itself and start over from the beginning, which will proceed to call the function again, and again, and again, and on and on. In this case, we don't have any way to stop it. This is called an infinite loop, one that will continue forever without stopping. Your server will die a flaming death.


Lesson 1: How to NOT create an infinite loop

Let me tell you right now. You will write a script that implements an infinite loop. You will test it. Your server will die. It's guaranteed. No programmer can say that they have never accidentally written an infinite loop. (RANDOM NOTE: All windows programs are in fact infinite loops. Your SPHERE server is an infinite loop.) In a SPHERE script, however, here's what happens:

  1. The function is called.
  2. Some stuff takes place
  3. The function is called from within itself. Being a good scripting language, it records where it left off so it can go back later. This is called the stack.
  4. Go back to number 1.

This "stack" builds up very very quickly, and soon the server cannot allocate any more memory for it, and will crash when it tries. Fun stuff, I tell you.


Anyways, here is one way to avoid creating an infinite loop. Let's say we want to make a function that executes SRC.SYSMESSAGE Hello World 35 times. Here would be an example of how one could do this:


[FUNCTION recurse_hello]
IF (<ARGN1> < 1)
    RETURN 1
ENDIF
SYSMESSAGE Hello World
RECURSE_HELLO <EVAL <ARGN1> - 1>
RETURN 1


Then, in another script, we would execute this command: SRC.RECURSE_HELLO 35


Remember what ARGN is from the previous chapter? It's the argument to the function stored as a number. Initially, as you can see, it's 25 because we made it be that way. However, every time the function calls itself, or "recurses", it sets ARGN1 to be one less than itself. Here's the step-by-step analysis of this:

  1. The function is called. ARGN1 is 35 because we said so.
  2. It checks to see if ARGN1 is less than one. If it is, we immediately RETURN 1 and set off the chain reaction that stops the recursive function.
  3. The next part should be fairly obvious. We're sending a SYSMESSAGE to the default object. Because we used SRC when we initially called the function, the default object is SRC.
  4. This is where the recursion takes place. The function calls itself with an argument ONE LESS than the current one. This starts the whole thing over at step 1. This is a NEW FUNCTION CALL, remember. The original function call STILL EXISTS and the program will "rewind" back down the stack to that location later. That is why I have a RETURN 1 after the function call.


That's your example of a recursive function. It isn't very practical. Let's look at a more practical example. See if you can figure it out for yourself. This is courtesy of Belgar, for the most part:


[FUNCTION pack_to_bank]
IF (<FINDLAYER.21.FINDCONT.0.UID> == 0)
    RETURN 1
ENDIF
FINDLAYER.21.FINDCONT.0.CONT = <FINDLAYER.layer_bankbox.UID>
PACK_TO_BANK
RETURN 1


(As you can see, we don't always need an ARGS to make a function loop. In this case, we use a backpack with an unknown number of items inside and only stop when the pack no longer contains items.)


Recursive functions are very useful. Be sure you don't overuse them, though! Remember, while a script is running, YOUR SERVER IS FROZEN. If a recursive function takes too long to complete, your server will lag. A good method is to make sure that no function should be looping more than about 500 times. (Actually other server emulators such as POL have a mechanism to catch "runaway scripts" like this and halt them in their tracks.)