Please enable JavaScript to view this site.

A-Shell Development History

Language enhancement adds new collection type:

MLIST(varstr)

MLIST(varx)

MLIST (short for "Multi-level LIST") is a bidirectional linked list, with the added feature that each element may contain a link to a child MLIST. Aside from the links, each element may contain a variable length string (varstr) or blob (varx). The MLIST is effectively a kind of tree structure, useful for representing a variety of real-world data such as disk directories and XML/JSON documents, where the ability to maintain a particular order and to efficiently insert, delete, and splice elements or branches at arbitrary points is required.

Unlike the ORDMAP and ORDMAPM collections, there is no direct access to an element by its key; there is only direct access to the first and last elements in the list, with the ability to iterate forwards, backwards, or down from there. The order of the elements in the list is preserved based on the order in which you build it.

As with other collection types, an MLIST is created with the DIMX statement:

DIMX $M, MLIST(VARSTR)

DIMX $M, MLIST(VARX)

 

The syntax for assigning and accessing elements is similar to that for ORDMAP, modeled on single-dimension array syntax. But instead of using string keys for the array index as in ORDMAP, the MLIST uses various special dot-variables which act as pseudo-keys. In addition, there are several new dot-statements and dot-functions and dot-members to perform operations particular to MLISTs that cannot be easily represented with existing statements and functions. These are summarized below, following by descriptions and syntax for the various operations.

Dot-Variable

Description

.FRONT

first element, e.g. ? $M(.FRONT)  

.BACK

last element, e.g.  ? M(.BACK)

.PUSHFRONT

add new element to front, e.g. $M(.PUSHFRONT) = X

.PUSHBACK

add new element to end, e.g. $M(.PUSHBACK) = X

Dot-Statement

Description

.POPFRONT

remove first element from list, e.g. .POPFRONT $M()

.POPBACK

remove last element from list, e.g. .POPBACK $M()

.SPLICE

splice elements from one list to other

.SORT

sort elements in list by value, e.g. .SORT $M()

Dot-Function

Description

.REF($$i)

returns a reference to iterator location, used when modifying an element by iterator, e.g. $M(.REF($$i)) = X

.BEFORE($$i)

specifies insertion position, e.g $M(.BEFORE($$i) = X

Dot-Member

Description

.SUBLIST

specifies sublist of element, e.g. $$i.SUBLIST, or $M(.BACK).SUBLIST

.KEY

$$i.KEY is equivalent to .KEY($$i)

 

All of the following descriptions and examples will assume the prior definition of these two MLISTs:

dimx $m, MLIST(varstr)

dimx $m2, MLIST(varstr)

 

Adding Elements

New elements may be added to the front or back of a list using array assignment syntax equivalent to that used for the ORDMAP collection, except instead of string keys, you must use one of the special dot-variable pseudo keys:

$m(.PUSHFRONT) = X     ! add new element to front; assign value X

$m(.PUSHBACK) = X      ! add new element at back; assign value x

 

You may also insert an element into the middle of the list using the special dot-function .BEFORE($$i). In the following example, we scan the list until locating an element whose value is "something", and then insert a new element before it:

foreach $$i in $m()

    if $$i = "something" then

        $m(.BEFORE($$i)) = x    ! insert new element before $$i position

        exit

    endif

next $$i

 

Modifying existing elements uses the same syntax as adding or inserting new elements, except with a different set of dot variables and dot functions:

$m(.FRONT) = y         ! change value of first element to y

$m(.BACK) = y          ! change value of last element to y

 

To update the value of an element by iterator, you must use the special dot function .REF($$i) to return to updateable reference to the iterator (since $$i by itself is a read-only copy of the value of the element).

foreach $$i in $m()

    $m(.REF($$i)) = $$i + "y"  ! update value of each element

next $$i

 

Deleting Elements

Elements can be deleted by assigning the special value .NULL to them, using the element modification syntax given above. For example:

$m(.FRONT) = .NULL              ! delete the first element

$m(.BACK) = .NULL               ! delete the last element

 

foreach $$i in $m()

    if $$i = "something" then

        $m(.REF($$i)) = .NULL   ! delete the element

        exit                    ! always exit foreach loop

    endif                       ! after removing an element, as it

next $$i                        ! may invalidate the iterator

 

In addition, you can use the following dot-statements to delete:

.POPFRONT $m()                  ! delete first element

.POPBACK $m()                   ! delete last element

 

Retrieving elements

X = $m(.FRONT)                  ! retrieve value of first element into X

X = $m(.BACK)                   ! retrieve value of last element into X

 

foreach $$i in $m()             ! access by iterator

    X = $$i                     ! X receives value of iterated element

next $$i

 

Splicing lists

One or more elements from one list can be spliced on to another using the dot-statement .SPLICE. Note no elements are actually allocated or deleted in this operation; they are just moved from the source list to the destination list. The general syntax for the .SPLICE statement is:

.SPLICE dst-list-ref, src-list-ref {, count}

If count (number of elements to move) is not specified, the operation moves all of the elements starting from the src-list-ref position. The list-ref parameters may be list base references, e.g. $m(), or they may be references to a particular element by iterator reference, e.g. .REF($$i). For example:

.SPLICE $m(), $m2()

The above case splices the entire $m2() list to the end of the $m() list. Afterward, the $m2() list will be empty (but still valid).

.SPLICE $m(), $m2(), 1

Same as the previous case except only the first element of $m2() is spliced to the end of $m().

To splice to some position other than the end of the destination, specify a position via an iterator reference. For example, to move append the 3rd and 4th items from $m2() the end of $m1()...

count = 0

foreach $$i in $m2()

    count += 1

    if count = 3 then                ! 3rd iterated item

        .SPLICE $m(), .REF($$i), 2   ! splice 3rd & 4th items

        exit                         ! iterator corrupted after splice! must exit!

    endif

next $$i

 

The .REF($$i) iterator technique also works for the destination, i.e. to insert at a destination other than the end of the target list. To splice from an arbitrary Xth position in the source to the Yth position in the destination, use a double-nested foreach loop:

! splice count elements from position srcpos in m1$() to dstpos in m2$

idst = 0                         ! counter for dest position

foreach $$i in $m2()

    idst += 1

    if idst >= dstpos then       ! $$i is the dst splice-to position

        isrc = 0

        foreach $$j in $m1()

            isrc += 1

            if isrc >= srcpos    ! $$j is the src splice-from position

                .splice .ref($$i), .ref($$j), count

                exit             ! must exit after splice!

            endif

        next $$j

        exit                     ! must exit after splice!

    endif

next $$i

 

Note: as with other modifications to a collection, the use of .SPLICE corrupts the iterator; thus you must exit from the foreach loop after the .SPLICE operation. (In the above example, we have to exit from both loops.)

Multi-level list operations: The sublists in an MLIST structure are themselves MLISTs, so all of the above operations work the same way on sublists. However, you do need a special dot-member, .SUBLIST, to specify when you are referring to the sublist for an element, rather than to the element as a member of the parent list.

~~~~======= !=== <this first method doesn't yet work> === ~~~~=======

There are two ways to create a sublist, both involving the use of the special .SUBLIST qualifier. The first is to splice elements from another list on to the .SUBLIST member of an element in the destination list, for example:

.SPLICE $m(.BACK).SUBLIST, $m2()

The above statement splices the list $m2() as a sublist of the last element in $m(). Note that without the .SUBLIST modifier, the $m2() list would have been spliced on to the $m() list, just before the last element.

foreach $$i in $m()

    if $$i = "something" then

        .SPLICE $$i.SUBLIST, $m2, 3

        exit

    endif

next $$i

 

In the above example, we scan the $m() list for an element with value "something", and then splice the first 3 elements of the $m2() list as a sublist of that target element.

~~~~~~~~~======= !=== <this method works> === ~~~~~~~~~=======

The second way to create a sublist involves passing it as a parameter to a function or procedure, which is dealt with in a separate section below, after the discussion of deleting sublists.

To delete a sublist, assign .NULL to the target element, either using one of the dot-variable pseudo-keys, or using an iterator to target the element:

$m(.BACK) = .NULL      ! delete sublist from last element of $m()

 

foreach $$i in $m()

    if $$i = "something" then

        $$i.SUBLIST = .NULL

        exit

    endif

next $$

 

The above examples delete the sublist without deleting the element itself. Deleting the element will also delete any sublist(s) attached to it.

Parameter Passing

MLISTs can be passed by reference to functions and procedures. This is similar to the way it works with ORDMAPS (see 1520.0) except that with MLISTs there is also a variation for passing sublists.

To pass a regular MLIST to a function or procedure, use the following syntax (nearly identical to that for ORDMAPs except for the "AS" clause):

call Proc( $m() )   ! passing the entire $m list by reference

 

Procedure Proc( $mloc() as MLIST(varstr) )

    $mloc(.PUSHBACK) = "new element"

EndProcedure

 

The above example adds the element "new element" to the MLIST $m(). Note that the local list $mloc() is effectively just an alias for the $m() list. As with passing ORDMAP arrays and other DIMX arrays by reference, you have to initialize the array first in the calling routine.

The pre-initialization requirement does not apply to sublists though, which are automatically initalized when passing them to another routine:

call Proc( $m(.back).SUBLIST )

In the above example, the SUBLIST for the last element of the $m() list is passed to the Proc procedure (given in the previous example). Note that the procedure does not know, or care, that the caller passed a sublist rather than an MLIST base reference; they are indistinguishable from the perspective of the procedure. Also note that since the sublist gets auto-initialized if necessary, the same syntax works regardless of whether the sublist initially exists.

You can also pass a sublist of an iterator, e.g.

foreach $$i in $m()

    if $$i = "something" then

        call Proc($$i.sublist)

    endif

next $$i

 

In the above example, for each element in the MLIST $m() whose value is "something", we pass the sublist to the Proc procedure, which adds a "new element" to the end of that sublist.