Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Looped Lists vs. Lotsa Ifs

Neard Harbinger
Linuxed Gadgeteer
Join date: 30 Sep 2005
Posts: 29
06-07-2006 23:37
Ever gone back to a project you thought was done and found something majorly wrong with it? Yep. I've got a script I've been working on, and I've got a color function in it. You pass it a color name and it returns the vector. I was looking at it, and realized I had it doing the same thing twice, in two different ways. Dunno how that ended up. But that got me to wondering, which way is more efficient? So, I'm posting the bits of code and want to get everyone's opinion.

First of all, there's the version that steps through a list of colors with a for loop:
CODE
vector color(string colorname)
{
list clrString = ["blue","red","green","grey","gray","white"];
list clrVector = [<0.0,0.0,1.0>,<1.0,0.0,0.0>,<0.0,1.0,0.0>,<0.6,0.5,0.5>,<0.6,0.5,0.5>,<1,1,1>];
integer clrCount = llGetListLength(clrString);
string strColor;
integer i;
if (llGetListLength(clrString) != llGetListLength(clrVector))
llOwnerSay("The list of colors do not match. The script will likely throw an error until this is repaired.");
vector retcol;
colorname = llToLower(colorname);
retcol = <0,0,0>; //If we don't end up with a legit color, give black.
for (i = 0; i < clrCount; i++)
{
strColor = llList2String(clrString, i);
if ( colorname == strColor)
retcol = llList2Vector(clrVector, i);
}

//And send the result
return retcol;
}

(Yes, I realize if you look it doesn't have all the colors the IFs do. Apparentally I started this part, forgot all about it, and added the IFs later. Go figure.)

And, the version that uses the IFs:
CODE
vector color(string colorname)
{
vector retcol;
colorname = llToLower(colorname);
//We need a switch. But they don't exist. So we get a lof of Ifs.
//The below line would be the default case in a switch.
retcol = <0,0,0>; //If we don't end up with a legit color, give black.
if (colorname == "blue")
retcol = <0.0,0.0,1.0>;
if (colorname == "red")
retcol = <1.0,0.0,0.0>;
if (colorname == "green")
retcol = <0.0,1.0,0.0>;
if (colorname == "grey" || colorname == "gray")
retcol = <0.6,0.5,0.5>;
if (colorname == "white")
retcol = <1,1,1>;
if (colorname == "cyan")
retcol = <0,1,1>;
if (colorname == "magenta")
retcol = <1,0,1>;
if (colorname == "orange")
retcol = <1,0.5,0>;
if (colorname == "pink")
retcol = <1,0.5,1>;
if (colorname == "purple")
retcol = <0.5,0.0,0.5>;
if (colorname == "yellow")
retcol = <1,1,0>;
if (colorname == "maroon")
retcol = <0.5, 0.0, 0.25>;
if (colorname == "mocha")
retcol = <0.5, 0.25, 0.0>;
if (colorname == "olive")
retcol = <0.5,0.5,0.0>;
if (colorname == "aqua")
retcol = <0,0.5,1>;
if (colorname == "lime")
retcol = <0.5,1,0>;
if (colorname == "teal")
retcol = <0,0.5,0.5>;
if (colorname == "plum")
retcol = <0.5,0,0.5>;

//And send the result
return retcol;
}


That's the code for each one. In reality, it's layed out a little different, and more mixed up, but that splits it into the parts for each one (and of course there's some overlap, like the return line).

So, which is more efficient? A for loop through a list, or a long stack of IFs? I think I know the answer, because lists are historically slow.

There's also the question, if I set it to just return the value in each for iteration or if statement, would that make it run faster by not going on through all the other steps? Or does LSL keep going anyhow? (in other words, instead of the "retcol = ..." and "return retcol", what if I just use "return ..." in each statement?
Sheila Plunkett
On The Prowl!
Join date: 25 Dec 2005
Posts: 67
06-07-2006 23:44
Hmm.. why don't you simply do a llListFindList, get the result (if -1, wrong color was given, else it returns the index where it was found), then use this index to get the color value from the color list?

CODE

vector defaultcolor = <1,1,1>;

vector color(string colorname)
{
list clrString = ["blue","red","green","grey","gray","white"];
list clrVector = [<0.0,0.0,1.0>,<1.0,0.0,0.0>,<0.0,1.0,0.0>,<0.6,0.5,0.5>,<0.6,0.5,0.5>,<1,1,1>,defaultcolor];
vector mycolor = llList2Vector(clrVector,llListFindList(clrString,[colorname]));
return mycolor;
}


This returns the right color vektor, or default color if the name is not found. (the llListFindList returns -1, which, if used on llList2Vector, _should_ return the last element of the list, which is why the clrVector has one additional entry at it's end)

*mew*
Sheila!
Catherine Omega
Geometry Ninja
Join date: 10 Jan 2003
Posts: 2,053
06-08-2006 02:43
From: Sheila Plunkett
Hmm.. why don't you simply do a llListFindList, get the result ... then use this index to get the color value from the color list?
I do this a lot in my scripts. It's a good idea, so far as I can tell.
_____________________
Need scripting help? Visit the LSL Wiki!
Omega Point - Catherine Omega's Blog
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
06-08-2006 03:22
I do it a lot too.

I've never speed tested it though, but it makes amending the script etc. so much easier and with a bit of smart use can stop your lists getting stupidly long which is when they really hog the resources.
Neard Harbinger
Linuxed Gadgeteer
Join date: 30 Sep 2005
Posts: 29
06-08-2006 04:07
From: Sheila Plunkett
Hmm.. why don't you simply do a llListFindList, get the result (if -1, wrong color was given, else it returns the index where it was found), then use this index to get the color value from the color list?
...
Sheila!


That's a very good idea, actually. It looks like it would be more efficient, too...less steps, less lines, therefore less processing (in theory).

I'm the kind of scripter that learns as they go. One problem with that, of course, is that you don't always know all the stuff that would make your code run better and more efficient. I didn't know much about stepping through lists because I'd never really done it before, so I hadn't learned the functions for it. (The list stuff I posted was the first time I'd really done much, and I ended up forgetting it was there and adding the ifs instead! :P)
Sheila Plunkett
On The Prowl!
Join date: 25 Dec 2005
Posts: 67
06-08-2006 04:21
It should run quite a bit faster, yes.

You can do a whole lot with clever use of Lists. Actually a lot of simple string operations that are lacking in SL can be "faked" using lists. And much more. Lists are, imho, the most powerful things LSL has.

The only downside of lists is that they use far too much memory, for my taste ^.^ But can't be helped much.. *mews*

So, glad if I could help you ^.^

*mew*
Sheila!
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
06-08-2006 08:52
As far as Ican tell, all of the ll* functions are done in a single frame by the sim. This means that instead of many operations for your llListFindList replacement, it occurs in one frame.

Put more simply, llList2String 'costs' about the same for a scripter as llListFindList. The sim has a miniscule amount more load, but it has nearly infinite power compared to an LSL script.
ed44 Gupte
Explorer (Retired)
Join date: 7 Oct 2005
Posts: 638
06-08-2006 17:43
Hi Neard

I think keknehv and Sheila are right in recommending the highest available LL functions to do anything. However, the repeated if statements will take a long time to execute. When a match is found the rest of the code will still be scanned.

If you use "if x else if y else" etc constructs you will find that the compiler will run out of stack space very quickly, so most code I have seen uses "return result" statements at each if level so as not to execute the following if statements when a match is found.

Of course this goes against good coding practices which recommend one entry and one exit in and out of each function, but that is the way of LSL! LSLint is great for identifying compiler stack problems!

Happy scripting!

Ed
Kermitt Quirk
Registered User
Join date: 4 Sep 2004
Posts: 267
06-08-2006 20:07
What a coincidence. I was just doing this exact same thing last night. I used a technique very similar to the code Shiela posted except for one small variation. If mine fails to find the color in the list I then try to convert the input string to a vector. That way they can use a color name, or a color vector, and finally if the string wasn't a vector the result will end up as black anyway (because if you try to cast a string to a vector, it'll return a zero vector if the string wasn't actually a valid vector). Depending on your implementation this may or may not be a wise thing to do.

Something like this...
CODE
vector color(string colorname)
{
list clrString = ["blue","red","green","grey","gray","white"];
list clrVector = [<0.0,0.0,1.0>,<1.0,0.0,0.0>,<0.0,1.0,0.0>,<0.6,0.5,0.5>,<0.6,0.5,0.5>,<1,1,1>];

integer colorIndex = llListFindList(clrString,[colorname]);
if (colorIndex == -1)
return (vector)colorname;
else
return llList2Vector(clrVector, colorIndex);
}
Neard Harbinger
Linuxed Gadgeteer
Join date: 30 Sep 2005
Posts: 29
06-10-2006 14:20
From: Kermitt Quirk
What a coincidence. I was just doing this exact same thing last night. I used a technique very similar to the code Shiela posted except for one small variation. If mine fails to find the color in the list I then try to convert the input string to a vector. That way they can use a color name, or a color vector, and finally if the string wasn't a vector the result will end up as black anyway (because if you try to cast a string to a vector, it'll return a zero vector if the string wasn't actually a valid vector). Depending on your implementation this may or may not be a wise thing to do.


Oh, wow. My next step was going to be a way to figure out how to do that, let people pass vectors. :D I've got a way right now, that's outside of the function. But I was wanting to create a fully independent function that I can drop into other scripts and call it...in other words, all I do is call the function and it does all the work figuring out the color!

Thanks! :D
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
06-10-2006 17:21
Just one thing, don't use this for all the scripts in a linked set-- that would be wasteful. If at all possible, have one script do this processing, and simply pass a vector to everything else. The sim will thank you for it.

Not that I'm assuming you'd do that, just putting out a warning to anyone reading this.
Neard Harbinger
Linuxed Gadgeteer
Join date: 30 Sep 2005
Posts: 29
06-10-2006 23:25
From: Keknehv Psaltery
Just one thing, don't use this for all the scripts in a linked set-- that would be wasteful. If at all possible, have one script do this processing, and simply pass a vector to everything else. The sim will thank you for it.

Not that I'm assuming you'd do that, just putting out a warning to anyone reading this.


That makes sense to me. Why have five prims doing the same thing if you can do it in one and pass it through linked messages? To be honest, I prolly would've just been lazy and dropped the script into each prim...But when I think about it, I go back and try and keep it as lag-free as possible.

Another project I'm working on, for instance, I've closed up every listen after it's done with it (mostly on dialogs). It's not much, but it's the small steps that move you towards big things.
Angela Salome
Registered User
Join date: 6 Oct 2005
Posts: 224
06-11-2006 04:00
From: Neard Harbinger
Another project I'm working on, for instance, I've closed up every listen after it's done with it (mostly on dialogs). It's not much, but it's the small steps that move you towards big things.

Be sure you llListenRemove listeners on a timer if they're ignored, otherwise you could stackup open listeners and crash the script with enough accumulated ignored llDialogs.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
06-11-2006 04:43
you can get kermits code to take up less space by storing the lists as strings and reducing the floats down to the bare essentials. Course not very readable. Optimizing out the integer isn't a good idea, may result in a memory collision.

CODE
vector color(string colorname)
{
integer colorIndex = llListFindList(llString2CSV("blue,red,green,grey,gray,white"),[colorname]);
return (vector)llList2String(llString2CSV("<0,0,1>,<1,0,0>,<0,1,0>,<.6,.5,.5>,<.6,.5,.5>,<1,1,1>,"+colorname), colorIndex);
}
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Neard Harbinger
Linuxed Gadgeteer
Join date: 30 Sep 2005
Posts: 29
06-11-2006 19:53
From: Angela Salome
Be sure you llListenRemove listeners on a timer if they're ignored, otherwise you could stackup open listeners and crash the script with enough accumulated ignored llDialogs.


If I assign the same variable name to the listen, would they still add up? Every time I open a dialog I use the same variable for the new llListen (ie integer DialogListen = llListen(...)). Would that automatically close the old one or just make it so you can't close it?
Joannah Cramer
Registered User
Join date: 12 Apr 2006
Posts: 1,539
06-11-2006 20:56
From: Neard Harbinger
If I assign the same variable name to the listen, would they still add up? Every time I open a dialog I use the same variable for the new llListen (ie integer DialogListen = llListen(...)). Would that automatically close the old one or just make it so you can't close it?

The return value you get from llListen() is just a handle, pointer of sorts, to allow you to tell the system which exactly --out of possibly many-- listen filters that you have running, you want to remove/suspend.

Storing this handle in single variable doesn't do anything to filters themselves, it just overwrites the reference you already had to earlier filter in that variable, with a new one.

You can picture it like this: you're a boss of intelligence agency, and can have number of spies working for you. But these workers get their names changed all the time between jobs for security reasons, so you can never rely on one of them being always called John, Mark, etc. Now when you create a listen filter, this is basically you yelling in your spy room "hey someone go and watch over <whatever>" and you get a "sure boss, Mark will do it" in return... so later you can tell "hey Mark, you can have a break for a bit now" and have Mark stop spying, while John and whoever else still keeps working on their own tasks.

When you use single variable to store the handle, it's like you use a chalkboard to store name of your most recently assigned spy, and each new spy you assign erases that chalkboard, and writes their own name on it for you... but it doesn't make the other, earlier assigned spies stop their work.

ok, came out kinda convoluted but hope it explains that ^^;;
Angela Salome
Registered User
Join date: 6 Oct 2005
Posts: 224
06-12-2006 04:21
From: Neard Harbinger
If I assign the same variable name to the listen, would they still add up? Every time I open a dialog I use the same variable for the new llListen (ie integer DialogListen = llListen(...)). Would that automatically close the old one or just make it so you can't close it?

That doesn't close the old one, unless you've changed states. If you do this repeatedly, your script will cause errors after around the 64th time, I believe.
Neard Harbinger
Linuxed Gadgeteer
Join date: 30 Sep 2005
Posts: 29
06-14-2006 11:29
Makes sense. The variable just says how to find the listen later, but doesn't do anything to it.

I've got some cleaning up to do, then.

Thanks, everyone, you've been very helpful!