Jump to content

List Pointers


Xiija
 Share

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

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

Recommended Posts

Has anyone come up with any inventive ways to do pointers in LSL ?

I need to cycle thru a list of lists ! lol

currently dealing with this grunginess...

       integer x;
       integer len = llGetListLength( Jazz );
       for( x = 0; x < len; ++x )
       {   string node = (string)(x+1);
           string url  = llList2String(Jazz,x);
           webStations = llJsonSetValue (webStations, ["Jazz",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Jazz",  "NUM-" + node, "URL"], url); 
       }
       // ====================
       integer x2;
       integer len2 = llGetListLength( Rock );
       for( x2 = 0; x2 < len2; ++x2 )
       {   string node = (string)(x2+1);
           string url  = llList2String(Rock,x2);
           webStations = llJsonSetValue (webStations, ["Rock",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Rock",  "NUM-" + node, "URL"], url); 
       }
       // ========================
       integer x3;
       integer len3 = llGetListLength( Reggae );
       for( x3 = 0; x3 < len3; ++x3 )
       {   string node = (string)(x3+1);
           string url  = llList2String(Reggae,x3);
           webStations = llJsonSetValue (webStations, ["Reggae",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Reggae",  "NUM-" + node, "URL"], url); 
       }
       // ================================
       integer x4;
       integer len4 = llGetListLength( Country );
       for( x4 = 0; x4 < len4; ++x4 )
       {   string node = (string)(x4+1);
           string url  = llList2String(Country,x4);
           webStations = llJsonSetValue (webStations, ["Country",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Country",  "NUM-" + node, "URL"], url); 
       }
       

 

Edited by Xiija
Link to comment
Share on other sites

Yes, but I use JSON.

In my case, a "pointer" is a unique value that identifies a "data instance" that I store in JSON. I use it as a "key" in some JSON, then as a "pointer" in other JSON to that "key" data.

You could do the same with lists.

I use GUID's. Those are big, you could use something else depending on your needs.

 

Link to comment
Share on other sites

5 hours ago, Xiija said:

Has anyone come up with any inventive ways to do pointers in LSL ?

I need to cycle thru a list of lists ! lol

currently dealing with this grunginess...


       integer x;
       integer len = llGetListLength( Jazz );
       for( x = 0; x < len; ++x )
       {   string node = (string)(x+1);
           string url  = llList2String(Jazz,x);
           webStations = llJsonSetValue (webStations, ["Jazz",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Jazz",  "NUM-" + node, "URL"], url); 
       }
       // ====================
       integer x2;
       integer len2 = llGetListLength( Rock );
       for( x2 = 0; x2 < len2; ++x2 )
       {   string node = (string)(x2+1);
           string url  = llList2String(Rock,x2);
           webStations = llJsonSetValue (webStations, ["Rock",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Rock",  "NUM-" + node, "URL"], url); 
       }
       // ========================
       integer x3;
       integer len3 = llGetListLength( Reggae );
       for( x3 = 0; x3 < len3; ++x3 )
       {   string node = (string)(x3+1);
           string url  = llList2String(Reggae,x3);
           webStations = llJsonSetValue (webStations, ["Reggae",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Reggae",  "NUM-" + node, "URL"], url); 
       }
       // ================================
        integer x4;
       integer len4 = llGetListLength( Country );
       for( x4 = 0; x4 < len4; ++x4 )
       {   string node = (string)(x4+1);
           string url  = llList2String(Country,x4);
           webStations = llJsonSetValue (webStations, ["Country",  "NUM-" + node ], node); 
           webStations = llJsonSetValue (webStations, ["Country",  "NUM-" + node, "URL"], url); 
       }
       

 

Well, truth be told, I see no problems with your implementation.

You might be able to reuse the variables x and len, since they doesn't seem to be used outside the for() loop.

An alternative will be to leverage LSD, say by encoding the key as "_d:mus:" + genre + "-" + node

Using LSD, you gain 2 benefits (which may or may not be applicable to your use case): One, it is immediately persistent (it will survive llResetScript() as well!) And two, it is globally available immediately, not only for the script in which the LSD KVP was made, but also for all other scripts part of the same linkset. If your script is pushing the limits of the 64 KiB space, then it might be a good idea to just split the script into two, communicating between them using link messages and LSD.

Edited by primerib1
Link to comment
Share on other sites

 

examples showing the principle of using indexing as "pointers" in a list are here:

On 4/13/2019 at 8:16 PM, Mollymews said:

 

this one is a little bit awkward to do in LSL as we don't have memory pointers, structs or classes. We can tho store a tree in a list (or a string) and work with it as a tree.

as wrote the problem case asks us to serialise a tree structure to a string then deserialise the string to a tree. Effectively saving and restoring it from say a file or notecard. In the LSL case because we can use a list, then save/restore can be done simply with llDumpList2String and ParseString2List

this said, trees are useful for stuff like paths in quest/games, nested menu systems, etc etc

here is one way to use a list to manage an unbalanced binary tree according to the principles contained in the problem case. This method traverses the tree using a non-recursive stack method. A depth-first traversal. Non-recursive stack methods (and queue methods for breadth-first trees) can be a little bit easier to follow in their code construction. Probably also easier to visualise their memory usage

in this traverse method the path is: go left, until cannot go any further left, then go right, when cannot go any further right then go back. When we start at the root of the tree we will end up back at the root after visiting every child node at least once.

we define a tree node to be 4 adjacent list elements. (similar to a stride)

Element 1 : pointer to the parent of the node. The root node is its own parent (0)
Element 2 : pointer to the right child node. Pointer is 0 when there is no child node
Element 3 : pointer to the left child node. Pointer is 0 when there is no child node 
Element 4 : data value stored in the node

example code:
 


//list index: 0 1 2 3       4 5  6 7         8  9 10 11       12 13 14 15       16 17 18 19       20 21 22 23
list tree =  [0,8,4,"Root", 0,0,12,"A Node", 0,20,16,"B Node", 4, 0, 0,"C Node", 8, 0, 0,"E Node", 8, 0, 0,"F Node"]; 

// the traversal path as wrote for this tree is: > Root > A Node > C Node > A Node > Root > B Node > E Node > B Node > F Node > B Node > Root

// other nodes can be added (or removed) as you like. The list order of nodes is unimportant, whats important is that node pointer values do point to a valid list index


list stack;

stackPush(integer ptr)
{  // add a child node to the stack
   stack += llList2List(tree, ptr, ptr + 3);
}

integer stackPop()
{
    integer ptr = llList2Integer(stack, -4); // get the parent node of the topmost node
    stack = llDeleteSubList(stack, -4, -1);  // remove the topmost node from the stack
    return ptr;  
}

integer stackPeek()
{
    integer idx = -2;
    integer ptr = llList2Integer(stack, idx);
    if (!ptr)  // if no left child then get right child if any
        ptr = llList2Integer(stack, --idx);
    if (ptr) // signal that we have taken this path
        stack = llListReplaceList(stack, [0], idx, idx);
    return ptr;  // when ptr == 0 then there is no child 
}


default
{
   state_entry()
   {
      string path;  
      integer node = 0;  // begin at root
 
      stackPush(node);
      while (llGetListLength(stack))
      {
         path += (" > " + llList2String(tree, node + 3));
         node = stackPeek();
         if (node)  // is a child node
            stackPush(node);
         else
            node = stackPop(); // node is now the parent node
      }
      
      llSay(0, path);
   }
}

 

and here

On 10/28/2021 at 12:01 AM, Mollymews said:

a conversation about sparse arrays here

led to the topic about the inefficiencies of using strings to store variable length records in the form

list table =
[
   "a|b|c",
   "d|e",
   "f|g|h|i",
   "y|z"
]

which prompted me to have a play.  A example of a general purpose 2-dimensional sparse array as a table with variable length records that preserve list element types

i think is ok, but if it breaks I will fix it if you let me know

fix:  ACTION_FIND. Was finding last occurrence of pattern, not the first

/*
   example of
     general purpose sparse array table with helper functions
     in row column format where a row (sublist) can have any number of elements (columns)
   
   row indexing is 0-based same as list 
   for negative indexing use: rowcount-1, rowcount-2,rowcount-3, etc
   
   usage examples in touch_start below
   
   fix: ACTION_FIND. Was finding last occurence of pattern, not the first
   
   placed in public domain
*/
 
integer ACTION_APPEND  = 0;   // table += [some,data]
integer ACTION_INSERT  = 1;   // llListInsertList
integer ACTION_REPLACE = 2;   // llListReplaceList
integer ACTION_DELETE  = 3;   // llDeleteSubList
integer ACTION_FIND    = 4;   // llListFindList 

list table;               // data table
list rows;                // pointers to 1st element of each row
integer currentrow = -1;  // updated on action. is -1 when table is empty
integer rowcount;         // = length of list rows
integer tablecount;       // = length of list table. - 

list fetch(integer row)   // llList2List
{   // fetch/get a list of elements (columns) in row
    if ((row > -1) & (row < rowcount))
    {
        integer e = tablecount - 1;
        if (row < rowcount - 1)
            e = llList2Integer(rows, row + 1) - 1;
        return llList2List(table, llList2Integer(rows, row), e);
    }
    else
       return [];  // return empty when row is out of bounds. bounds in [0 .. rowcount-1]       
}

integer action(integer act, integer row, list data)
{   // default to -1 fail / not found. OnSuccess return currentrow
    integer result = -1;   
    if (act == ACTION_APPEND)
    {   
        if (data) 
        {    
            rows += [tablecount];
            table += data;
            tablecount = llGetListLength(table);                 
            result = currentrow = ++rowcount;               
        }
    }
    else if ((row > -1) & (row < rowcount))  // row in bounds else fail
    {
        integer r = llList2Integer(rows, row);
        integer e = tablecount - 1;
   
        if (act == ACTION_DELETE)
        {              
            if (row < rowcount - 1)
                e = llList2Integer(rows, row + 1) - 1;
            else
                --currentrow;
            table = llDeleteSubList(table, r, e);
            rows = llDeleteSubList(rows, row, row);
            --rowcount;
            e = -(e - r + 1);
            result = currentrow;           
        }
        else if (data)  // fails when data is empty list
        {           
            if (act == ACTION_INSERT)
            {                
                table = llListInsertList(table, data, r);
                rows = llListInsertList(rows, [r], row++);
                ++rowcount;
                e = llGetListLength(data);
                result = currentrow;               
            }
            else if (act == ACTION_REPLACE)
            {
                if (row < rowcount - 1) 
                    e = llList2Integer(rows, ++row) - 1;   
                table = llListReplaceList(table, data, r, e);  
                e = llGetListLength(data) - 1 - (e - r);
                result = currentrow;
            } 
            else if (act == ACTION_FIND)
            { 
                if (row)  // can be memory expensive, but it does enable findNext 
                    e = llListFindList(llDeleteSubList(table, 0, r - 1), data);   
                else  // row = 0. search entire table
                    e = llListFindList(table, data); 
                if (~e) // not found when e = -1
                {   // get row number, disallow when found data pattern crosses row boundary
                    e += r;
                    integer continue = TRUE;
                    for (r = row; (r < rowcount) & continue; ++r)
                    {
                        integer ee = tablecount - 1;
                        if (r < rowcount - 1)
                            ee = llList2Integer(rows, r + 1);
                        if ((e >= llList2Integer(rows, r)) & (e < ee)) 
                        {   // note fetch is called. This could be calculated 
                            result = llListFindList(fetch(r), data); 
                            if (~result)
                            {   // found wholely in row
                                result = currentrow = r;
                                continue = FALSE;
                            }
                        }
                    }
                }
            }
        }     
            
        if (act != ACTION_FIND)
        {   // update rows pointers and tablecount
            for (r = row; r < rowcount; ++r)
                rows = llListReplaceList(rows, [llList2Integer(rows, r) + e], r, r); 
            tablecount = llGetListLength(table);
        }   
    }
    // return currentrow after action
    // return -1 when row out of bounds or not found
    return result;  
}


emptyTable()
{   // empty table and reset supportives
    table = [];
    rows = [];
    currentrow = -1;  
    rowcount = 0;
    tablecount = 0;    
}

default
{
    touch_start(integer num_detected)
    {
        llOwnerSay("BEGIN");
        
        emptyTable();
        
        // fill with some data. Data can be any type. 
        // little strings used to easier see what is happening
        action(ACTION_APPEND, 0, ["aa", "ab", "ac"]);
        action(ACTION_APPEND, 0, ["ba", "bb"]);
        action(ACTION_APPEND, 0, ["ca"]);
        action(ACTION_APPEND, 0, ["da", "db"]);
        action(ACTION_APPEND, 0, ["ea", "eb", "ec"]);
        action(ACTION_APPEND, 0, ["fa", "fb"]);       
       
       llOwnerSay("append: [" + llDumpList2String(table, ",") + "]");
       llOwnerSay("rows: [" + llDumpList2String(rows, " ") + "]");          
      
       action(ACTION_DELETE, 1, []); 
       llOwnerSay("delete row 1: [" + llDumpList2String(table, ",") + "]");
       llOwnerSay("rows: [" + llDumpList2String(rows, " ") + "]");          

       action(ACTION_INSERT, 3, ["ma", "mb", "mc"]);
       llOwnerSay("insert new row 3: [" + llDumpList2String(table, ",") + "]");
       llOwnerSay("rows: [" + llDumpList2String(rows, " ") + "]");          
       
       action(ACTION_REPLACE, 0, ["ya", "yb"]);
       action(ACTION_REPLACE, rowcount - 1, ["xa"]);
       llOwnerSay("replace row 0 and last row: [" + llDumpList2String(table, ",") + "]");
       llOwnerSay("rows: [" + llDumpList2String(rows, " ") + "]");          
       
       list elements = fetch(3);
       llOwnerSay("fetch elements in row 3: [" + llDumpList2String(elements, ",") + "]");
       
       // the pattern to find, can be any number of elements 
       integer i = action(ACTION_FIND, 0, ["db"]);   
       llOwnerSay("find row for [db] in entire table. Row = " + (string)i);
       
       i = action(ACTION_FIND, 4, ["da", "db"]); 
       llOwnerSay("find row for [da,db] from row 4. Row = Not Found = " + (string)i);
       
       i = action(ACTION_FIND, 0, ["db","ma"]); 
       llOwnerSay("find cross boundary fail for [da,ma]. Row = Not Found = " + (string)i);     
       llOwnerSay("END");
   }
}

 

 

 

Link to comment
Share on other sites

thanx all for the helps, I used csv and could do both :)

an example for anyone looking for list pointers...

string webStations;
list   jazz_a;
list   jazz_b;
 
default
{
    state_entry()
    {  // JSON
        webStations = llList2Json( JSON_OBJECT, [] ); 
        
       // or json2list from web    
       jazz_a = [
        "https://breakz-high.rautemusik.fm/?ref=radiobrowserinfo",
        "https://jam-high.rautemusik.fm/?ref=radiobrowser",
        "https://mixtapes.superstreams.de/",
        "https://mixtapes.electronicssounds.com/",
        "http://stream.laut.fm/ruffneck-smille?ref=radiode",
        "http://o89fm.de:8000/main",
        "https://stream.0nlineradio.com/latin?ref=radiobrowser",
        "https://stream.0nlineradio.com/remix?ref=radiobrowser",
        "http://strm112.1.fm/reggae_mobile_mp3"
    ];
     jazz_b = [
        "2-https://breakz-high.rautemusik.fm/?ref=radiobrowserinfo",
        "2-https://jam-high.rautemusik.fm/?ref=radiobrowser",
        "2-https://mixtapes.superstreams.de/",
        "2-https://mixtapes.electronicssounds.com/",
        "2-http://stream.laut.fm/ruffneck-smille?ref=radiode",
        "2-http://o89fm.de:8000/main",
        "2-https://stream.0nlineradio.com/latin?ref=radiobrowser",
        "2-https://stream.0nlineradio.com/remix?ref=radiobrowser",
        "2-http://strm112.1.fm/reggae_mobile_mp3"
    ];
     string ja   = llList2CSV(jazz_a);
     string jb   = llList2CSV(jazz_b);
     webStations = llJsonSetValue (webStations, ["MAIN", "Jazz_a" ], ja); 
     webStations = llJsonSetValue (webStations, ["MAIN", "Jazz_b" ], jb); 
      
     string urls = llJsonGetValue (webStations, ["MAIN", "Jazz_a" ]); 
     llOwnerSay("\ndata:  " + urls);
     list   j1   = llCSV2List( urls );
     llOwnerSay("\nJSON data check: " + llList2String( j1, 0) );
     
     list   jmain = ["Jazz_a", "Jazz_b"];  // ================================= store list names as strings
     string jINDX = llList2String( jmain,0);   // ============================= get a string name
     string main  = llJsonGetValue (webStations, [ "MAIN" , jINDX ]);   // ==== call JSON string/list with a string name
     llOwnerSay("\nMain:\n" + main + "\n Touch to get LsD data");
     
    }
    touch_start(integer total_number)
    {   // LsD
        string j1 = llList2CSV(jazz_a);
        string j2 = llList2CSV(jazz_b);
        llLinksetDataWrite( "j1" ,j1 );//====================================== make a key string
        llOwnerSay( "\nLsD data check: \n" + llLinksetDataRead("j1")); //====== check it is stored
        
        list   jmain   = ["j1", "j2"];  // ==================================== store key names as list of strings
        string jINDX   = llList2String( jmain,0);  // ========================= get a string name
        llOwnerSay( "\n J data: \n" + llLinksetDataRead( jINDX ));      // ==== call LsD key with a string name
        
        list   jlist_1 = llCSV2List( llLinksetDataRead( jINDX ) );     // ===== seperate string into csv
        llOwnerSay("\nLsD first entry:\n " + llList2String( jlist_1, 0) );  
    }
}

 

Edited by Xiija
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 448 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...