List

Instead of arrays, LSL uses lists. The list type is exactly what it sounds like: a heterogeneous list of the other data types. Lists are created via comma-separated values (CSV) of the other data types. enclosed by square brackets: "[" and "]". See below for examples of the syntax.

Because they store multiple value types, lists are not accessed with square brackets like arrays are. They are read by accessor functions that specify the type of value attempting to be retrieved (e.g. llList2Integer) and written with accessor functions that insert or replace values (or ranges of values) based on new list variables (e.g. llListReplaceList).

Note: while the LSLcompiler will only accept code with a maximum of 72 items in a list, lists can actually support as many items as a script'smemory will allow. If a list of more than 72 predefined items is needed, just concatenate (combine) two predefined lists into a new one: longlist = firstpart + secondpart;As of 1.20.4, this no longer seems to be true. A list with at least 100 is possible, though I haven't tested to see just how many elements I can initialize a list to. - BlueJeansAndRainJune 5, 2008 - Server 1.22.1.88473 | Client 1.19.1 (4): I was able to create a global list containing 1,081 null integers and a local list containing 2,318 null integers before receiving "(0, 0) : Byte code assembly failed -- out of memory". - CoryFimicoloud

Lists cannot contain other lists, so using them like a multidimensional array is not possible natively (although several ways around this are available, notably this script). Also, strided lists can be used.

It's also worth noting that with functions, lists are not referenced; they are duplicated locally. This means that, as an example:

iUseLists(list input){...}

handed the list:

list oh_hi = [data here];

results in "input" being a duplicate of the data held in "oh_hi", rather than oh_hi being operated on. This is important, as many higher level languages (the most obvious in my mind being java) operate on the original list (contextually, array). In practice, this means that a scripter attempting to use this functionality erroneously could easily end up flooding their script memory with a large list, and not having their expected results in the original list as well.

This voodoo magic will allow appending new elements in a memory efficient fashion (thanks to BlindWanderer):

myList = (myList=[]) + myList + ["new_item"];

Don't ask me why but I can save 3-4KB of memory with adding 90 elements. - PsykePhaetonIs this broken as of Aug-21-2007? It doesn't appear to work - VagnerSousaBR "This voodoo magic seems to work fine. Just pay attention to the "new_item" square brackets when it's already returned as a list type in a function like llParseString2List(). I got a stack-heap collision error when I was composing an huge list by the usual way. This way has worked fine."This works because LSL makes a temporary copy of all variables in an expression before it evaluates it. Say you're concatenating two 2kb lists: You originally use a total of 4kb of memory. The normal method of concatenation would copy them (then requiring a total of 8kb), then concatenate them (total of 12kb), then copy them into the original variable (14kb), then destroy all the temporary copies. The "voodoo magic" here is that after the temporary copies are made for evaluating the expression, the original variable is cleared, requiring extra memory only during the copy. This gives the rest of the operation a significant amount of breathing room. -DunselHodgsonThis trick is no longer needed with Mono. See the Q&A section for evidence. - MimikaOh

It seems that this trick can also be used to great effect when passing lists to functions that change the list and then overwrite the same list with the new copy, such as llDeleteSubList. From my testing on a large sample list using calls to llGetFreeMemory(), this appears to use no memory at all, where the normal way of doing it would use over 2000 bytes. I'm not sure if these figures are really correct, but it certainly made it a lot less likely to cause a stack/heap collision when the list was very big, so it must be saving at least some memory, and the resulting list was as expected. The syntax for this is as follows:

Note: This temporarily clears the original list variable before saving the result back into it, so if you save the result in a different variable instead, the original one will end up empty after this. --EddyOfarrel

EddyOfarrel results appear correct that the 'voodoo' trick works in the list modification functions. My memory usage results can be found on my user page. - CoryFimicoloud

Q:Does anyone know whether any of this applies to scripts compiled with Mono? (From my initial testing, it seems like using this kind of trick makes no difference to the amount of memory used by the operation in a Mono script, and may actually cause the script to use more memory to start with. I suspect this probably makes sense, as Mono probably doesn't do things in such an inefficient way to start with, but it would be nice if someone who knows more about how exactly this trick and Mono work could confirm this.) -EddyOfarrel

Strided Lists

Since lists cannot contain items of the list type, the only way to get something similar to a multidimensional array is using strides. A strided list is a regular list with items grouped together in a fixed layout.

Say you wanted to make a visitor tracker and keep track of visitor's names, time spent on your land, and the date they visited last. You could use a strided list, where each stride consists of three items:

More examples of functions dealing with strided lists can be found on this page. LibraryStridedLists

Q & A

Q:Does this function have O(1) time? I want to use it a lot on some big lists.A:No, it takes much longer with growing list size. Better get used to LSL being painfully slow when manipulating large data sets (strings by far being the worst).Re-Question:So, does that mean llListInsertList and llDeleteSubList actually make copies, i.e., reallocate, the entire list they're operating on?Re-Answer:Yup. LSL is a pass-by-value language. Any information passed to a function is copied to that function's stack-frame before the function is run. This means that in practical terms, you need twice as much memory to insert or delete a sublist. (See the notes on the llListInsertList and llDeleteSubList pages to eliminate some confusion about the "manipulation" they do.)

Q:Why are there so many llList2* functions?A:LSL2 doesn't support run-time typing, meaning you have to actually specify the type of each variable. The Lindens could have implemented a single function, llList2String, then required explicit typecasting, as in (integer)llList2String(foo, 0). However, that isn't as memory- or CPU-efficient as llList2Integer(foo,0) (yes, really) and the amount of work required for the Lindens to implement seperate functions for each type was negligible.You can still typecast values stored in a list, of course, but it's advised to use the llList2* functions, unless it's a vector or rotation stored as a string, in which case you should cast to a vector or rotation after using llList2String. llList2Vector and llList2Rotationdo not cast from strings automatically. See the next question for more.

Q:Have the old problems concerning the odd behavior of llList2Vector and llList2Rot when requesting a TYPE_STRING element from the list been fixed yet?A:No. If a list element is a string containing text that would otherwise be directly typecast to a vector or rotation, It will not be converted. The resulting vector or rotation will be ZERO_VECTOR or ZERO_ROTATION respectively. This is a bug. To get around it, use llList2String to retrieve elements from your list, then cast them to vectors or rotations as needed: (vector)llList2String(positions,2);

Lists can be conveniently passed as a single string using the CSV functions. For example, this is useful for sending complex data between objects via listen or within linked objects with llMessageLinked. Or not, if you would be passing data with vectors, rotations, or strings with commas. llDumpList2String and llParseString2List are recommended for safe passing of lists. For an example of this method see ExampleListConversion.

I've tried running tests on memory usage, comparing mono and LSL. They both seem to use the same memory usage for storing lists. And trying to get some kind of formula about the memory usage of a list of a given length that's populated purely with integers, I'm getting very schizophrenic results. A list consisting of just one integer uses 112 bytes of memory. 10 integers also used 112 bytes of memory. 100 integers used 1988 bytes. A list of 1000 integers used 20028 bytes. What does it seem that there's an upwards curve in the memory usage as lists grow longer? And when the heck are we going to get access to proper arrays? - LuccaKittyLuccaKitty, typically list allocates memory for certain number of elements (usually 10) immediately to avoid memory fragmentation and the cost of resizing, and that's why you don't see increase in memory usage until the 11th element is inserted in the list. - DomchiUnderwood

I think Eep is more obsessed with HTML conventions than with standard typesetting conventions. Yes, titles are capitalized. Yes, they are often underlined. No, we don't care that it takes .18 seconds longer to retrieve a non-cached page...

Also, HTML conventions do not specify that links be underlined. That is a MicroSoft HTML formatting default that some other browsers support by default, not a part of any specification relating to the HTML standards.

Lists appear to be managed with reference counting nodes. This is as of 1.9.0(21). Maybe it used to be as bad as this wiki page describes, but from the looks of it, lists can be quite efficient when being manipulated. I wish somebody who KNOWS how this works would post details. It has a huge impact on the quality of code people produce.

We need functions that will allow us to iterate. Currently, the only way to iterate the entire list is to use a loop with an incrementing integer index. The problem with this is the list must be traversed from the beginning for each and every iteration of your loop.

As you can see, offering a way to iterate through nodes without losing your place in the list would greatly reduce the drain on CPU resources and speed up list traversals.
Anyone that agrees should forward this to Linden Labs. Thanks.

Here's something for someone with too much free time to look into: I've got a list (see "New Improved Particle Script Sample!" by Jopsy Pendragon for what it is) and a lot (maybe 60) of global variable declarations. It compiled fine, until I added one more global variable declaration (12KB free memory at this point). It then gave me a syntax error, and pointed to the last line of my list in state_entry(). Deleting the last element of the list fixed the syntax error, as did splitting the list in half and concatenating it. I could also remove the new global integer to make it compile.

The compiler obviously has its more limitations and quirks apart from those documented here and there.

So, concating a list doesn't work anymore??? Seems all the code examples here are not working for me. Can we confirm and remove that?

-- c-69-252-195-0.hsd1.nm.comcast.net (2007-10-06 14:45:44)

How memory-efficient (or otherwise) are lists compared to several discreet variables? I have several 'data sets' that I wish to communicate as concatenated strings. It is easy to convert between lists and strings with llDumpList2String() and llParseStringKeepNulls(). Is it worth the extra step of parsing-out the elements to discrete variables or should I just keep the list?