Jump to content

Logic question - A question about jumping around functions


Xander Lopez
 Share

You are about to reply to a thread that has been inactive for 1544 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

I have some complicated series of functions that call another function. And this is quite confusing to me.

Let's say i have the following script.

function1() { llOwnerSay("This chair is made of wood"); ... call more functions... }
function2() { llOwnerSay("This chair is made of wood"); ... call more functions... }

functionA (){

if (apple == 1) watermelon = 1;
if (orange == 1) pineapple = 1;

if (chair == 1) function1();
else if (desk == 1) function2();

}

functionB () { llSetTimerEvent(2); }

callMoreThanOneFunctions() { functionA();functionB();}

 

So here is the confusing part. Let's say I called the function callMoreThanOneFunctions(). This would execute functionA first, and then execute functionB upon completion of the functionA.

I want to execute callMoreThanOneFunctions() However, if (chair == 1) or (desk == 1) are true, I want to cancel calling functionB.  I would like to avoid calling function1 and functionB when (chair == 1).

How can I program this?

 

 

 

Link to comment
Share on other sites

40 minutes ago, Xander Lopez said:

integer functionA ()
{  if (apple == 1) { watermelon = 1; }
    if (orange == 1) { pineapple = 1; }
    if (chair == 1) { function1(); }
    else if (desk == 1) { function2() }
    else { return(FALSE); } // didn't do it
    return(TRUE); // did it

}

functionB () { llSetTimerEvent(2); }

callMoreThanOneFunctions()
{    integer didit = functionA();
     if (!didit) { functionB();}
}

 

Link to comment
Share on other sites

This and another related topic have raised a question or two in my head that I can't find answered in the wiki. I suspect I already know it, or at least I've always assumed it:

Does a user-defined function execute fully (to the return) before an event which has occurred after the function execution began? I have always assumed it does.

I assume (again that fallacy) that if a change of state occurs within a user-defined function the remainder of the function is scrapped, and when the new state returns to the state in which that user function was being called, it resumes at state_entry and does not pick up in the user function?

Link to comment
Share on other sites

26 minutes ago, Profaitchikenz Haiku said:

Does a user-defined function execute fully (to the return) before an event which has occurred after the function execution began?

Yes.

26 minutes ago, Profaitchikenz Haiku said:

if a change of state occurs within a user-defined function the remainder of the function is scrapped, and when the new state returns to the state in which that user function was being called, it resumes at state_entry and does not pick up in the user function?

Yes, that's right. This is the only way that comes to mind to really purge the call stack, which is what the OP seems to want to do under certain conditions tested in FunctionA.

The logic specified is confusing, though; I'm not sure when function1 is ever supposed to be called if it should "avoid calling function1 and functionB when (chair == 1)."

[EDIT: Ack, pressed "Submit" too soon. I meant to point out that changing state from within a user function is extremely tricky business. I'm not 100% sure this stack purging effect can actually be obtained. In fact, reading the wiki, I'm now not sure there's any way to change state from a function at all anymore.]

Edited by Qie Niangao
Link to comment
Share on other sites

28 minutes ago, Profaitchikenz Haiku said:

Does a user-defined function execute fully (to the return) before an event which has occurred after the function execution began? I have always assumed it does.

I assume (again that fallacy) that if a change of state occurs within a user-defined function the remainder of the function is scrapped, and when the new state returns to the state in which that user function was being called, it resumes at state_entry and does not pick up in the user function?

Events will stay on the "event queue" until the script's call-stack is empty. (Meaning, all functions have finished executing.)

If the current state is changed from within a function, that function will immediately return with a zero-value and will not continue when the state changes back to previous.

vector function()
{
    integer i;

    while (++i < 10)
    {
        llOwnerSay("loop counter: " + (string)i);

        if (i == 5)
        {
            state new;
        }
    }
    return (<1,1,1>);
}

default
{
    state_entry()
    {
        llOwnerSay("default state");
        vector returned = function();
        llOwnerSay("function returned " + (string)returned);
    }
}

state new
{
    state_entry()
    {
        llOwnerSay("new state");
        state default;
    }
}

The above script is an infinite loop from 1 to 5.

Edited by Wulfie Reanimator
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

12 minutes ago, Wulfie Reanimator said:

If the current state is changed from within a function, that function will immediately return with a zero-value and will not continue when the state changes back to previous.

This is interesting, and totally not what I expected. First, this disproves that wiki passage I cited about there being no way to change state from within a user function: obviously this script does that. But second it also does something kind of weird: It unwinds the call stack before executing the state change, even completing the default state_entry handler before executing the change to state new. The more I think about this, the messier it seems: where does that impending state change get stored while the previous call stack is completing? I'm missing something; it can't be this magical.

Link to comment
Share on other sites

10 minutes ago, Qie Niangao said:

This is interesting, and totally not what I expected. First, this disproves that wiki passage I cited about there being no way to change state from within a user function: obviously this script does that. But second it also does something kind of weird: It unwinds the call stack before executing the state change, even completing the default state_entry handler before executing the change to state new. The more I think about this, the messier it seems: where does that impending state change get stored while the previous call stack is completing? I'm missing something; it can't be this magical.

I should've put an asterisk in there somewhere. You're not supposed to be allowed to change state from within a function. These don't compile:

function()
{
    state new; // ERROR : Global functions can't change state
}

function()
{
    integer i;

    if (i == 0)
    {
        // stuff
    }
    else
    {
        state new; // ERROR : Global functions can't change state
    }
}

if and else if work fine. It's a bug. I couldn't explain why/how it works or what the implications are. @Rider Linden?

Edited by Wulfie Reanimator
  • Thanks 1
Link to comment
Share on other sites

Recursive functions work. So do functions called by other functions. Works all like expected.
But you must not do state changes from functions! That's forbidden! :D
If you ignore that and do it anyways -  then don't complain if things don't work like expected anymore :D
I would not use it since that can break everytime LL changes something in the script execution. In the past I could solve all my scripting needs without using a crowbar.

  • Like 1
Link to comment
Share on other sites

4 minutes ago, Profaitchikenz Haiku said:

I hope that there aren't regions slowly grinding to a halt because we're experimenting with recursion, stack-heap collisions, infinite loops, and state-changing in global functions.

There is nothing wrong with recursion or infinite loops, they should be considered "standard practice" as far as the LSL engine is concerned. Stack-heap collisions don't hurt the sim either; but not having them definitely would. The jury's still out on hot-swapping states...

Edited by Wulfie Reanimator
Link to comment
Share on other sites

I'm not worried about recursion, although the industries I worked in prohibited them or any other coding technique that could not be predicted, it was the realisation that giving a user-defined function a long and complicated task would effectively freeze all other events, it's the equivalent to having no interrupts whatever in a PLC, there's no way to force it back to a responsive state except for the reset method.

In practice then, should a script go into a non-responsive state, the events for it will begin to pile up and probably get discarded on a first-in first-overboard protocol.

What has piqued my curiousity here is something I've been trying to investigate for several weeks, it's a non-repeatable problem that nevertheless occurs several times a week: at login, I make my way to a parcel monitor and touch it, expecting a dialog to pop up. Usually it does, but sometimes no dialog pops up. I touch the prim a couple more times in case I missed it, but no dialog. I then do things such as click on a door to make sure touch itself is working, then touch something like the train, and almost always get a dialog. I clear this away, and there on the screen behind it are all the queued dialogs that should have been presented to me when I first clicked the monitor. I can't produce this on demand, and once cleared, it behaves as normal, so I can't log in on a different viewer to see if this is a bit of TPV-misbehaviour or what. I suspect it's a touch event issue, something has convinced the script that it's too busy to respond to the touch immediately. It does have global functions, but no states, and no recursion, and the lines I added to monitor free memory show there's loss of free memory.

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

18 minutes ago, Profaitchikenz Haiku said:

In practice then, should a script go into a non-responsive state, the events for it will begin to pile up and probably get discarded on a first-in first-overboard protocol.

The event queue can hold 64 events, and the 65th event is ignored/discarded.

21 minutes ago, Profaitchikenz Haiku said:

at login, I make my way to a parcel monitor and touch it, expecting a dialog to pop up. Usually it does, but sometimes no dialog pops up.

I think this is explained by the way a busy sim schedules scripts to be executed. For example, when you're in a sim with "30% scripts run" per frame and you click on an object while it's not in that 30%, it won't even get an event because it isn't running. This is just untested speculation.

Link to comment
Share on other sites

But my region is a 99.99% scripts run, and if there's another 3 avatars on the rest of the place I start to feel crowded ...

It does get the event because as I mentioned, they suddenly all arrive once I do manage to get a dialog working.

4 minutes ago, Wulfie Reanimator said:

The event queue can hold 64 events, and the 65th event is ignored/discarded.

The implication then is that once 64 have been queued, new ones get dropped, it's not going to scrap old ones in favour of fresh? Sounds like vulture-scheduling to me, it prefers the dead...

Link to comment
Share on other sites

7 hours ago, Profaitchikenz Haiku said:

at login, I make my way to a parcel monitor and touch it, expecting a dialog to pop up. Usually it does, but sometimes no dialog pops up.

Does this ever happen if you wait (and wait, and wait) until all objects are fully rezzed and all textures are loaded in the scene before touching the object? I suspect this may be the viewer trying to wade through the onslaught of data pumping into it upon arrival, but if it still does this after the texture console clears out, then it's clearly something else.

Link to comment
Share on other sites

11 hours ago, Wulfie Reanimator said:

if and else if work fine. It's a bug. I couldn't explain why/how it works or what the implications are. @Rider Linden?

this is pretty interesting. I had a play.  A hack round the if else if

function1(integer a)
{
    if (a) 
    {
        if (TRUE) state default;
    }
    else
    { 
        if (TRUE) state new;
    }
}

default
{
    state_entry()
    {
        function1(0);
        llOwnerSay("state default");
    }
}

state new
{
    state_entry()
    {
        function1(1);
        llOwnerSay("state new");
    }
}

this flips between states

Link to comment
Share on other sites

29 minutes ago, animats said:

That's how I'd expect it to work. When can't you cause a state change in a function?

as discussed above changing state in a global function shouldn't be possible, yet it can be done in this undocumented way as Wulfie showed

another thing about this is that when we place other code in function1() then it only does a state change once for each state and fails any subsequent call without generating any runtime error as far as I can tell

the only thing that I can see where this may be useful is in an app where we have a lot of states, and we want a simple way to flip between them.  Example:

switchstate(integer index)
{
   if (index == 0) state default;
   if (index == 1) state level1;
   if (index == 2) state level2;
   if (index == 3) state level3;
   if (index == -1) state abort;
}

 

 

 

 

Link to comment
Share on other sites

So this:

integer function(integer a)
{
    if (a)
    {
        state trueState;
     }
     if (TRUE)
     {
         state falseState;
    }
    return(-99);
}

default
{
    state_entry()
    {
        llOwnerSay("default state");
        integer returned = function(TRUE);
        llOwnerSay("function(TRUE) returned " + (string)returned);
        returned = function(FALSE);
        llOwnerSay("function(FALSE) returned " + (string)returned);
    }
    state_exit()
    {
        llOwnerSay("exiting default state");
    }
}

state trueState
{
    state_entry()
    {
        llOwnerSay("in trueState");
    }
}

state falseState
{
    state_entry()
    {
        llOwnerSay("in falseState");
    }
}

Does this:

Quote

default state
function(TRUE) returned 0
function(FALSE) returned 0
exiting default state
in falseState

What bugs me about all this is that the state change doesn't happen immediately, but rather control returns to the caller first, with the state change pending. In this example, that caller calls again, creating a different pending state change which eventually executes, apparently overwriting the earlier pending state change. I don't think I recognize this control flow from other languages.

Link to comment
Share on other sites

5 minutes ago, Qie Niangao said:

What bugs me about all this is that the state change doesn't happen immediately, but rather control returns to the caller first, with the state change pending. In this example, that caller calls again, creating a different pending state change which eventually executes, apparently overwriting the earlier pending state change. I don't think I recognize this control flow from other languages.

Which is probably why you're not supposed to be able to do it. The exact process hasn't been properly implemented, so the way it works now is just an artifact. I mean, "state changes" are an arbitrary implementation that's very specific to LSL, this could technically be the "proper implementation" if it wasn't for the fact that the compiler tries to stop us from even trying.

Edited by Wulfie Reanimator
  • Like 2
Link to comment
Share on other sites

On 1/5/2020 at 3:47 PM, Xander Lopez said:

I want to execute callMoreThanOneFunctions() However, if (chair == 1) or (desk == 1) are true, I want to cancel calling functionB.  I would like to avoid calling function1 and functionB when (chair == 1).

How can I program this?

getting back to the OP question

and ?assuming? that chair and desk are exclusive then

function1()
{
   ...something with chair...   
}

function2()
{
   ...something with desk ...   
}

functionB()
{
    ... something with timer ...
}

integer functionA()
{
   if (chair != 1)
   {
      function1();
   }
   else
   {
      if (chair != 1)  // this does nothing for chair == 1 but does for all instances of desk
        function2();    
   }
   return (chair == 1 || desk == 1);
}

 
callManyFunctions()
{
   if (!functionA())
       functionB();
}

personally I don't recommend writing code like this. Is pretty much a classic example of spaghetti with a spoonful of implication

best to separate out our working with chair and desk in functions of their own. The code will be a whole lot more manageable and readable when we do. Example:

callManyFunctions()
{
   if (we_are_working_with_chair)
      doStuffToChair();
   else if (we_are_working_with_desk)
      doStuffToDesk();
}

 

 

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1544 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...