Category talk:LSL List

From Second Life Wiki
Jump to: navigation, search

SVC-1710 Documentation Needed

SVC-1710 has been Closed with the resolution "Won't Fix". The LSO/Mono incompatibility needs to be documented. -- Strife (talk|contribs) 19:46, 1 March 2009 (UTC)

This recently came up on Jira, this really needs to be documented! -- Strife (talk|contribs) 10:43, 1 April 2012 (PDT)

I want to document the fact that the following two expressions are NOT the same somehow.

[1] list_var += [(string)key_var]; [2] list_var += (string)key_var;

Why does this matter? why consider the following line of code:

index = llListFindList(list_var, [(string)key_var] );

If method [1] is used before the line above,you will find your value, however if you use method [2], you will not.


After much frustration I wanted to share this in hope someone else will not fall into the pit, or if it is a bug, it can be put in Jira. I'll let one of the Wiki gods decide what to do with this. --Awsoonn Rawley

I'll look into it, this sounds like a bug. Those two expressions should be equivalent. -- Strife Onizuka 18:26, 3 June 2007 (PDT)
I've done some testing and I cannot replicate your problem. Are you aware that indexing starts at zero and not one? -- Strife Onizuka 15:56, 5 June 2007 (PDT)
try this --Awsoonn Rawley 04:35, 6 June 2007 (PDT)
default
{
    state_entry()
    {
        list lst;
        key  uuid = llGetKey();
        
        lst = ["bob", "tom", "jerry"];
        
        lst += (string)uuid;
        
        llSay(0, (string)llListFindList(lst, [(string)uuid]) );
    }
}

I would really just like some documentation on this page about how to index into a list at all. Thanks - Kendown Baroque 00:48, 28 August 2007 (PDT)


On Concatenation

I have read in many places that there are two ways to concatenate lists, the "standard" way and a memory-saving "voodoo" technique. The standard way looks like this:

Code: "Standard" List Concatenation
list myList = ["element1"];
myList += ["element2"];

The "voodoo" technique looks like this:

Code: "Voodoo" List Concatenation
list myList = ["element1"];
myList = (myList=[]) + mylist + ["element2"];

So, being a bit of a computer scientist, I decided to give it a test. Here's my test code:

Code: Test Code - List Concat Standard
integer REPS = 30;

list myList;

integer i;

default { // INIT
    state_entry() {
        llOwnerSay("Testing memory usage...");
        
        myList = [];
        for (i = 0; i < REPS; ++i) {
            myList +=                       ["test3.0", "test3.1", "test3.2", "test3.3", "test3.4", "test3.5", "test3.6", "test3.7", "test3.8", "test3.9"];
        }
        llOwnerSay("List Standard Short Format Post-Concat 10*" + (string) REPS + " compound: " + (string) llGetFreeMemory());
        
        myList = [];
        for (i = 0; i < REPS; ++i) {
            myList =               ["test3.0", "test3.1", "test3.2", "test3.3", "test3.4", "test3.5", "test3.6", "test3.7", "test3.8", "test3.9"] + myList;
        }
        llOwnerSay("List Standard Long Format Pre-Concat 10*" + (string) REPS + " compound: " + (string) llGetFreeMemory());
    }
}

Output:

  • Testing memory usage...
  • List Standard Short Format Post-Concat 10*30 compound: 4261
  • List Standard Long Format Pre-Concat 10*30 compound: 4261
Code: Test Code - List Concat Voodoo
integer REPS = 30;

list myList;

integer i;

default { // INIT
    state_entry() {
        llOwnerSay("Testing memory usage...");
        
        myList = [];
        for (i = 0; i < REPS; ++i) {
            myList = (myList=[]) + myList + ["test4.0", "test4.1", "test4.2", "test4.3", "test4.4", "test4.5", "test4.6", "test4.7", "test4.8", "test4.9"];
        }
        llOwnerSay("List Voodoo Post-Concat 10*" + (string) REPS + " compound: " + (string) llGetFreeMemory());
        
        myList = [];
        for (i = 0; i < REPS; ++i) {
            myList = (myList=[]) + ["test4.0", "test4.1", "test4.2", "test4.3", "test4.4", "test4.5", "test4.6", "test4.7", "test4.8", "test4.9"] + myList;
        }
        llOwnerSay("List Voodoo Pre-Concat 10*" + (string) REPS + " compound: " + (string) llGetFreeMemory());
    }
}

Output:

  • Testing memory usage...
  • List Voodoo Post-Concat 10*30 compound: 9795
  • List Voodoo Pre-Concat 10*30 compound: 9739

Results

Interesting results... A gain of 5534 to 5478 bytes of memory for these tests.

Remember, bigger numbers mean more free memory, therefore less memory being used.

Cron Stardust 00:14, 31 December 2007 (PST)

It should be noted that myList += anotherList; is better under Mono than myList = (myList = []) + myList + anotherList;, well, that is it uses a tiny bit less bytecode and is just as efficient on memory in practise. Likewise with myList = anotherList + myList; being just fine under Mono, however the "voodoo" method still works great for Mono function calls with lists that are to be overwritten, such as myList = llListReplaceList((myList = []) + myList, anotherList, 2, 2);, I've added some comments to this effect on the main article.
-- Haravikk (talk|contribs) 15:41, 8 October 2010 (UTC)

Structures and Embedded lists

The proferred rule of not being able to embed a list, or a structure in a list as being impossible, or that we are left with a rather clunky strided list as a replacement couldn't be farther from the truth. I offer the following:


list     OverLoaded = [];
integer  IntElement = 99;
string   StringElement = "You Can't Fool Me";
float    FloatElement = 3.1419;
key      KeyElement = NULL_KEY;


default
{
  state_entry()
  {
    Overloaded =[] 
     + (string)IntElement 
     + "," 
     + StringElement 
     + "," 
     + (string)FloatElement 
     + "," 
     + (string)KeyElement;
  }
  touch_start( integer num )
  {
     ParseListElement( Overloaded, 0 );
  }
}

Now what you have is a list with a single structured element.

["99,You Can't Fool Me,3.1419,00000000-0000-0000-0000-000000000000"]

Additional elements can be added to this list. Parsing, sorting, manipulating any portion of the structure is a simple matter of breaking it out. Once done, and upon manipulating any of the sub-elements, the uber-element needs to be manipulated with the available LSL functions in order to save it as sorted deleted or changed. I prefer this, however, to a thing I have seen fail, get scrambled, confused and just plain break. That being the strided list operations.


ParseListElement( list superlist, integer element )
{
  list    Temp = llCSV2List( llList2String( superlist, element );
  integer IntPart = (integer)llList2String(Temp, 0 );
  string  StrPart = llList2String( Temp, 1 );
  float   FltPart = (float)llList2String( Temp, 3 );
  key     KeyPart = (key)llList2String( Temp, 4 );
}

(please note the *lack* of giving in to the temptation to use llList2<anything_but_string_here>. There are still a few bugs in those, and am not given to trust that they will work. *KNOWN* issues with llList2Key have lead me not to use any of the other variations, and directly typecast the result myself.)

While this is not as simple as saying

struct MyStructure
 { 
  integer Int, 
  string  Str, 
  float   Flt, 
  key     Key;
 } 

it does accomplish the same thing. The only caveat is that if your string element actually contains commas you'd need to use llParseString2List with an appropriate seperator character rather than llCSV2List, but I have found that you can work around that by replacing actual commas in the strings with something else. Once again, sorting removing, replacing any single element of your structure involves the operation on the entire "super" list element, but is no more painful and far more trustworthy than a strided list. Not being one to leave things to chance, and knowing somewhat painfully and repeatedly that anything you haven't taken direct control over yourself is at the mercy, whims, and moods of LSL that day. A strided list is something which I have encountered as being subjectable to those far-flung LSL variations. This alternative measure will work... every time.

The rules for memory savings seem to have changed...

The proposed memory saving rules don't seem to be valid anymore... Take a look at this test code:

<lsl> list l; integer i = 2000; while (i--) l += [i]; llOwnerSay((string)llGetFreeMemory()); </lsl>

yields 29316 bytes of free memory. Now let's prepend the item (with brackets) (I will omit the first two lines and the last line from here on):

<lsl> while (i--) l = [i] + l; </lsl>

yields 21364 bytes. Certainly not better than the first method. Now take a look at the same code without the brackets:

<lsl> while (i--) l = i + l; </lsl>

yields 29752 bytes, about as much as in the first case. So it seems to me like prepending with brackets is no longer the preferred method for Mono? I used the first method in a pretty complex script and it saved me a lot of memory over the second method which I thought to be better for a long time now. In addition, the first method yields reproducible results while the second has a very, very big variance over successive runs (one time, 600 bytes are left, then again 10000 bytes).

By the way, what you store seems to have a huge effect as well. Let's compare two cases with random integers:

<lsl> while (i--) l += [(integer)llFrand(100)]; </lsl>

yields 29120 bytes while

<lsl> while (i--) l = [(integer)llFrand(100)] + l; </lsl>

yields only 5704 bytes left.

Ochi Wolfe 18:09, 24 August 2012 (PDT)

l = [(integer)llFrand(100)] + l is a totally different operation than l += [(integer)llFrand(100)]. The first puts the value on the front of the list, the second puts the value on the end of the list. It's likely given the list object Mono is relying upon does a poor job of doing random inserts and only does a good job with tail inserts. LSO does both equally poorly. Without looking at the article I can fully imagine that the advices needs to be forked into Mono and LSO advices. -- Strife (talk|contribs) 21:14, 27 August 2012 (PDT)