Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Can I have a function that processes a list identified by a variable?

AnnMarie Otoole
Addicted scripter
Join date: 6 Jan 2007
Posts: 162
03-22-2007 19:47
I've simplified my problem to an elementary situation just to explain the question.

Assume I have 3 lists already containing data:-
list SampleList1;
list SampleList2;
list SampleList3;

And I need a function to do an identical operation to any ONE of these lists without hard coding each list separately.
For example:-
I want to clear SampleList2 using a general purpose function that can be used to clear any list such as:-

ClearList(SampleList2);

using the defined function:

ClearList(WhichList2Clear) { //Where WhicList2Clear is a variable containing the list's name
WhichList2Clear= [];
}

This won't work because to define the name of the list, WhichList2Clear has to be a string variable but in order to carry out the clearing, it has to identify a list variable.

How do I use a string variable as the name of the list on which I want to process?
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
03-22-2007 20:30
From: AnnMarie Otoole
How do I use a string variable as the name of the list on which I want to process?
Sorry, this can't be done in LSL. Variable names are just for the convenience of the programmer and anyone else reading the code. Compiled bytecode doesn't retain those names.

What are you doing which you think needs this capability? There's probably another way to do it.
Keknehv Psaltery
Hacker
Join date: 11 Apr 2005
Posts: 1,185
03-22-2007 20:32
Actually, there are very good reasons for wanting to be able to do this, such as when manipulating large lists to avoid the memory limitation and you want to avoid an if-else construct. Unfortunately, you can't.

This is called "indirection", and is commonly supported by interpreted languages, but LSL is a compiled language, with the slowness of an interpreted language and the inflexibility of a compiled one.
AnnMarie Otoole
Addicted scripter
Join date: 6 Jan 2007
Posts: 162
03-22-2007 23:12
I have a bunch of lists that need adjustments where a common function could be used for all of them that would save duplicating the code numerous times. Whenever I find myself duplicating code, that is a flag to write a function to handle it but I couldn't make this one work.

Pregnant pause. (Grinding sounds, gears stripping, clutch burning).

Ah ha hang on everyone, hold the phone, I think I can still do it with a function that only works on a "named" list since I can't use a variable name to fetch it indirectly:-

I will create a global "Temporary" list that has the list modifying function customized just for it.
I should be able to copy the list to be modified to the temporary one, call the function to perform the manipulations, and the function can "return" it to the calling routine which can then put it back in the source list. The lists are complex but not very long so the overhead for copying twice should not be serious.

That should get calling the function back down to one line as intended. Something like:-

//(global)
list Templist;

//The manipulating function
Manipulate(list Templist, integer DataToProcessInList, etc.) {
- -- manipulate global list Templist data here - - -;

return Templist;
}

and call it like:-

SampleList2 = Manipulate((Templist = [] + SampleList2), Data1, Data2 etc);

I'll report back after trying it if anyone is interested. This was just thinking out loud, knowing my experience it will need some tweaking. I can see already that I didn't need to pass Templist to Manipulate() as a parameter since it is global now.
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
03-22-2007 23:38
From: AnnMarie Otoole
SampleList2 = Manipulate((Templist = [] + SampleList2), Data1, Data2 etc);
I'm not seeing the advantage over:

CODE

SampleList2 = Manipulate( ( SampleList2 = [] ) + SampleList2, Data1, Data2 );
Ashrilyn Hayashida
Registered User
Join date: 6 Jul 2006
Posts: 103
03-22-2007 23:46
Hmm.

If it works, maybe something like these global functions..
CODE

list ClearList(list lList) {
// functions here to clear the list supplied
return lList;
}

list SomethingElse(list lList) {
// Do something else to the list
return lList;
}


And when you need to use the functions:
SampleList1 = ClearList(SampleList1);
SampleList2 = SomethingElse(SampleList2);
Pale Spectre
Registered User
Join date: 2 Sep 2005
Posts: 586
03-23-2007 02:34
I think Ashrilyn is spot on... but :) understand that with: list function(list lList) you are declaring (and populating) an additional list for each of your functions. Lists are expensive.

So...

CODE
list temp;

list ClearList()
{
// functions here to clear list temp
}

list SomethingElse()
{
// do something else to list temp
}

:

list temp = [] + SampleList1;
ClearList();
SampleList1 = [] + temp;
temp = [];

list temp = [] + SampleList2;
SomethingElse();
SampleList2 = [] + temp;
temp = [];
...only costs one extra list for all functions.

The memory constraints can and will drive you mad. :p
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
03-23-2007 04:28
The null list declarations and concatenations ( [] + ) in the following lines:

CODE

list temp = [] + SampleList1;
SampleList1 = [] + temp;
list temp = [] + SampleList2;
SampleList2 = [] + temp;
don't gain you anything, they just take up code space and slow execution.

The point of the expression ( SampleList1 = [] ) + SampleList1 is to make a copy of the contents of SampleList1 to pass to a function, and then erase the contents of SampleList1, (since concatenation operations are evaluated right-to-left), so that you don't have 2 copies of that data taking up memory while the function you pass the copy to does whatever it does with the data.

The global "temp" list will take up at least 21 bytes at all times, whereas passing a copy as a function parameter should only use it while the function is executing.

Also, I don't see the point of writing a function to clear a list. Why not just assign a null list to it directly?
Pale Spectre
Registered User
Join date: 2 Sep 2005
Posts: 586
03-23-2007 05:19
The rather unintuitive: '= [] +' thing stems from this reference:

http://www.lslwiki.net/lslwiki/wakka.php?wakka=list

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

CODE
myList = (myList=[]) + myList + ["new_item"];
Don't ask me why but I can save 3-4KB of memory with adding 90 elements. - PsykePhaeton
Maybe I'm not understanding it properly.

From: Deanna Trollop
The point of the expression ( SampleList1 = [] ) + SampleList1 is to make a copy of the contents of SampleList1 to pass to a function, and then erase the contents of SampleList1, (since concatenation operations are evaluated right-to-left), so that you don't have 2 copies of that data taking up memory while the function you pass the copy to does whatever it does with the data.
Heh, I like that a lot, thank you for explaining it. I think that's worthy of a note in the Wiki.

From: Deanna Trollop
...whereas passing a copy as a function parameter should only use it while the function is executing.
Hm, that's also interesting as in the past I've spent a lot of time adding and then removing function parameters because of the memory hit. I may have to experiment.


From: Deanna Trollop
Also, I don't see the point of writing a function to clear a list. Why not just assign a null list to it directly?
I think you're missing the point. :) I've been assuming that these are just arbitrary examples exploring the way lists can be handled in conjunction with common functions. What the function actually does is not the issue.

One big problem is that - as has been commented on here before - because llGetFreeMemory doesn't work too well it's very difficult to tell just what does, and does not, free up memory. If there's a definitive reference I'd be very interested to see it.
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
03-23-2007 05:50
From: Pale Spectre
The rather unintuitive: '= [] +' thing stems from this reference
Yeah, it took me a while to wrap my head around it, too.


From: someone
Heh, I like that a lot, thank you for explaining it.
Welkies.


From: someone
What the function actually does is not the issue.
Granted, just seemed like an odd example.


From: someone
One big problem is that - as has been commented on here before - because llGetFreeMemory doesn't work too well it's very difficult to tell just what does, and does not, free up memory.
Oh, I feel ya. 'Been working on a project involving dynamic data storage that I want absolutely bullet-proof, since it will operate largely unattended, and unless the owner happens to be there when stack and heap collide (wasn't that a Def Leppard tune? :cool: ), they won't even know something went wrong, until complaints start rolling in. So I'm stuck with trying to manually track memory usage, and brute-force stress-testing to make sure my limiting features actually work as intended, and if not, finding what the actual limits of the system are.

It's kinda like mapping the Grand Canyon blindfolded, not knowing how close to the edge you are now, only how close you've been... until you fall over the cliff. :eek:
AnnMarie Otoole
Addicted scripter
Join date: 6 Jan 2007
Posts: 162
03-23-2007 06:34
My clear list was just a simplified example.

For those that missed why it is an advantage it is like:-

For list = Sample1, list < Sample9, list++
add Data to the nth element and remove from the list if less than zero or greater than y

Although my attempt copies each list to a temporary one, for processing, the lists have typically less than 10 elements plus it is only temporary so it can be cleared when done to recover memory.

Without the function, it would take 8 sets of identical straight coding.
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
03-23-2007 09:23
If your individual lists are of a fixed length, you could concatenate them into one strided list, making an unofficial 2-dimensional array, of sorts.

CODE

list MegaList; // 5 concatenated lists of 10 elements each
integer L; // Index of the first element of each sub-list
integer n = 3; // Do something with element #3 of each sub-list

for( L = 0; L < 50; L += 10 )
{
integer Data = llList2Integer( MegaList, L + n ) ;
// Manipulate Data, yadda yadda
}

Of course, the drawback of a single long list is you need to keep more memory free than you would for several smaller lists, since you need to pass a copy of the list to the llList2* functions to access the data, for which the prepended nullification trick can't be used... unless the data contained in the list can be discarded with the first read.
Brain Curry
Registered User
Join date: 15 Jun 2006
Posts: 9
03-23-2007 19:05
From: Keknehv Psaltery
\This is called "indirection", and is commonly supported by interpreted languages, but LSL is a compiled language, with the slowness of an interpreted language and the inflexibility of a compiled one.


You're freakin' kidding me, right?

Compiled? In what sense? You know LSL runs on a VM, right? And even if it don't now, it will soon. That's what CLR is. What does it mean that an "intepreted" language like Ruby has a compiler backend for CLR?

Is compiling to a VM any different than compiling to byte code like perl?

Yeah. What about compiled languages, like lisp, that do offer this flexibility? Or other "compiled" languages with halfway decent runtimes? Objective C, Smalltalk, Oberon, and, oh, btw, C++?

Here's the real skinny:

LSL is Turing complete.

That means that anything that can be implemented in any other Turing complete language can be implemented in LSL, by hook or by crook.

You want to dynamically manage lists? Fine. Then use things like associative arrays, where the head of the array is the key. Bang. Now you have a full hash, just like any other language that offers dictionary or hash-based lookup.

So, you want to do an operation on a list by name? No problem. You just have to know what kinds of operations you'll need to do, what kinds of data you'll need to support it, and how to get those data.

So, since LSL is so basic (and it really is that bad), you could, if you wanted to support arbitrary sub-list manipulation on a named list, tag a list with its name as the first element and the number of items as the second, followed by the actual list of items.

This allows you to store a list within lists. It's not so different from strided lists, except that instead of a constant sub-list length, you have a dynamic one define by the second element of the list.

From there, you can write a couple functions to abstract this if you need to:

clear_list_named("foo";);
append_to_list_named("foo", ["val1", "val2"]);

and so on. You just have to know how to traverse your one-dimensional array as an n-dimensional array. We've encoded that in the second element, so it's no problem.

Anyway, stop spreading lies about "compiled" vs. "interpreted". It's so meaningless as to be completely naive. Especially today. If you came at me with this in '86, I'd be much less likely to bite your nose off. I probably still would have, tho.
Brain Curry
Registered User
Join date: 15 Jun 2006
Posts: 9
03-23-2007 19:19
From: Pale Spectre
Lists are expensive.


Please correct me if I'm wrong, but I've been assuming standard linked-list behavior (IOW, datum + link to next).

In this case, "expensive" is a relative term. It's hard to radomly index a list (vs an array/vector), but it's perfectly simple (and damn near as fast) to read from it iteratively. Plus, it's much faster to insert and delete elements than a standard vector/array.

Unfortunately, LSL doesn't offer vector/array structures, so all we have are lists. And, frankly, if that's all we got we should use it. Turning a structure that naturally represents itself as list into a series of variables should be avoided.

If you have performance problems, fine, do what you need to to fix them. Until then, however, make the data structure as natural a representation of the inputs to the problem you're trying to solve as you can. If you want to walk a tree, use a tree to represent your nodes. If you don't have a tree in language, build it via functions and the capabilities in your language.

It can be slower, but for 99% of what you do, speed is not an issue. Mostly what you want to be able to do is change your code when you feel like it. When you structure your data towards your goal at a high level, it's easier to modify later on because you understand it better.

Premature optimization is the root of all evil.
Pale Spectre
Registered User
Join date: 2 Sep 2005
Posts: 586
03-24-2007 04:15
From: Brain Curry
In this case, "expensive" is a relative term.
I describe lists as being expensive simply because they have the capacity to hold a lot of data and consume your meagre 16kb memory allowance. Therefore you need to be careful how you code them because it's easy to unwittingly consume a lot of resource very quickly... which is partly the point of this thread.

Good list handling is essential to scripts that have to handle a lot of data.

I think that practical examples like those offered by Deanna Trollop are lot more useful than any 'academic' discussion on the Nature of LSL. LSL is what it is, and it works like it does. Bugger-all I can do about it really. :)
bucky Barkley
Registered User
Join date: 15 May 2006
Posts: 200
03-24-2007 11:47
From: Brain Curry

Here's the real skinny:
LSL is Turing complete.


Good thing that Alan Turning and the British didn't have to rely on LSL to break German codes in WWII.
_____________________
Bucky Barkley -- Mammal, Scripter, Builder, Lover