nested list

Introduction



A nested list is simply a list that occurs as an element of another list (which may of course itself be an element of another list, etc.).

Common reasons nested lists arise are:

  1. They're matrices (a list of rows, where each row is itself a list, or a list of columns where each column is itself a list).
  2. Lists are being used for what in other languages is known as structs, records, or tuples -- collections of data with a fixed structure.
  3. A tree is encoded as a list where the subtrees occur as elements (hence lists are nested as deeply as the tree is high).

[Discuss when using a nested list is important or useful - what are cases where it is a good solution]

Before Tcl 8.4, nested lists were quite difficult to work with -- getting nested elements required nesting lindex commands, and the nightmare of setting elements in nested lists without the help of lset is better forgotten -- and as a result older code often uses arrays even in cases where the first-class data status (being possible to pass by value) of a list would have been very useful.

[Discuss which Tcl built in commands can be used to build, search, and maintain the list]



LISP-style lists

In LISP, lists are built by linking "cons cells", which is simply a pair of values (typically implemented close to the hardware, e.g. as a C-struct of two pointers; a Tcl_Obj is a higher level concept) where the first (the head) by convention is the first list element and the second (the tail) is the rest of the list; in the last cell of a list the tail pointer is NULL. The following is (yet another) implementation of this, with Tcl lists of length 2 serving as cons cells. It is probably not of any practical interest.

FM: Maybe an higher concept level has some higher conceptuals properties. Who knows ? A Tcl'ers is always dealing with such things, so why do not have some proc to experiment with it ?

Regulars nested lists (same llength at each depth)

Nested list of constant length 2



nl2 package

set nl2 {
       {2lindex {translate between nest list index and list index}}
       {append {append at the end of the 2-length nested list variable (like lappend)}}
       {assign {assign each member of the 2-length nested list value to variables (like lassign)}}
       {concat {To do : concat for list work on non-list object too, so I haven't find what to do for this command}}
       {flat {convert a 2-length nested list value as a flat list}}
       {index {retrieve one or all element of the 2-length nested list value}}
       {insert {insert one element at the place specified to a 2-length nested list value - return a new value}}
       {iorder {return the list of the subindex in the order of a specific nested list}}
        is {
                {left {test if the given object value is a 2-length left nested list}}
                {right {test if the given object value is a 2-length right nested list}}
                {mixed {test if the given object value is a 2-length left nested list, whose elements are 2-length right nested list and vice-versa}}
        }
        {join {convert the nested list as a string in the style of join for list}}
        {left {make a left nested list with all arguments}}
        {length {return the depth of the nested list}}
        {merge {merge a right 2-length nested list with a left 2-length nested list to a middle 3-length nested list }}
        {merge-left {merge a right 2-length nested list with a left 2-length nested list to a left 3-length nested list}}
        {merge-right {merge a right 2-length nested list with a left 2-length nested list to a left 3-length nested list}}
        {merge-dict {merge a right 2-length nested list with a left 2-length nested list as a dict}}
        {range {return a range of elements of a 2-length nested list value ans return it as nested list}}
        repeat {
                left {like lrepeat but make a left 2-length nested list}
                right {like lrepeat but make a right 2-length nested list}
        }
        {reverse {reverse the order of a 2-length nested list value}}
        {rindice {return the recursif indice (nest indice) of a specific nested list}}
        {right {make a left nested list with all arguments}}
        {search {like lsearch for 2-length nested list}}
        {sort {like sort for 2-length nested list}}
        {set {set an element of a 2-length nested list variable}}
        {transpose {change a 2-length left nested list value in a 2 length right nested list value (and vice-versa)}}
        {type {return the type of the 2-length nested list, if any, return "" otherwise}}
        {{!type} {return the type "left" for a right 2-length nested list value, or "right" for a left 2-length nested list}}
}

namespace eval nl2 {
    proc 2lindex {type index} {
        ::set nl nl[::set len 2]
        ::set depth [expr {($index)/($len-1)}]
        ::set reste [expr {($index)%($len-1)}]
        return [list {*}[lrepeat [expr {$depth}] [$nl rindice $type]] [lindex [$nl iorder $type] $reste]]
    }
    proc append {L args} {
        # nl2 append ...
        ::upvar $L nl
        ::if {[::set type [nl2 type $nl]] ne ""} {
            ::set l [nl2 flat $nl]
            ::set nl [nl2 $type {*}[lappend l {*}$args]]
            return $nl
        } else {
            return -error -message "bad list type"
        }    
    }
    proc assign {L varname args} {
        # nl2 assign ...
        if {[nl2 is left $L]} {
            return [nl2 left {*}[uplevel [subst {lassign {[nl2 flat $L]} $varname $args}]]]
        } elseif {[nl2 is right $L]} {
            return [nl2 right {*}[uplevel [subst {lassign {[nl2 flat $L]} $varname $args}]]]
        }
    }
    proc concat {} {
        # nl2 concat ...
        # to do, but what ?
    }
    proc flat {L} {
        # nl2 flat ...
        ::if {[nl2 is left $L]} {
            ::for {::set i 1} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 0]} {
                ::lappend Res {*}[::lindex $L {*}$i]
            }
            return $Res
        } elseif {[nl2 is right $L]} {
            ::for {::set i 0} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 1]} {
                ::lappend Res {*}[::lindex $L {*}$i]
            }
            return $Res
        }
    }
    proc index {L args} {
        # nl2 index ...
        ::return [::lindex [nl2 flat $L] $args]     
    }
    proc insert {L index element args} {
        # nl2 insert ...
        if {[nl2 is left $L]} {
                ::return [nl2 left {*}[linsert [nl2 flat $L] $index $element {*}$args]]
        } elseif {[nl2 is right $L]} {
                ::return [nl2 right {*}[linsert [nl2 flat $L] $index $element {*}$args]]
        }  
    }
    proc iorder {type} {
        # nl3 iorder ...
        switch -- $type {
            left {return 1}
            right {return 0}
            default {return ""}
        }   
    }
    namespace eval is {
        proc left {L} {
            # nl2 is left ...
            ::set res 1
            ::if {[::llength $L] == 2 && [::llength [::lindex $L 0]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 2 && [::llength [::lindex $L 0]] == 2} {
                    ::set res [nl2 is left [::lindex $L 0]]
            } else {
                    ::set res 0
            }
            ::set res               
        }
        proc mixed {L} {
            # nl2 is mixed ...
            set res 0
            if {[set type [nl2 type $L]] eq ""} {return $res}
            set res 1
            foreach e [nl2 index $L] {
                set res [expr {$res && ([nl2 !type $e] eq $type)}]
            }
            return $res
        }
        proc right {L} {
            # nl2 is right ...
            ::set res 1
            ::if {[::llength $L] == 2 && [::llength [::lindex $L 1]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 2 && [::llength [::lindex $L 1]] == 2} {
                    ::set res [nl2 is right [::lindex $L 1]]
            } else {
                    ::set res 0
            }
            ::set res            
        }
        namespace export *
        namespace ensemble create
    }
    proc join {L {sz { }}} {
        # nl2 join ...
        ::join [nl2 flat $L] $sz
    }
    proc left {args} {
        # nl2 left ...
        ::set L [::list ""]
        ::set i 0
        ::foreach e $args {
            if {[::llength $e] != 1} {::set e [list $e]}
            ::lset L {*}$i [::list "" $e]
            ::lappend i 0
        }
        ::return {*}$L  
    }
    proc length {L} {
        # nl2 length ...
        ::set j 1
        ::if {[nl2 is left $L]} {
            ::for {::set i 0} {[::llength [::lindex $L {*}$i]]!=0} {::lappend i 0; incr j} {}
        } elseif {[nl2 is right $L]} {
            ::for {::set i 1} {[::llength [::lindex $L {*}$i]]!=0} {::lappend i 1; incr j} {}
        } else {
            ::set j 0
        }
        ::return $j
    }
    proc merge {L0 L1} {
        if {[nl2 is left $L0] && [nl2 is right $L1]} {
            ::set L [list {}]; ::set i {}
            foreach e0 [nl2 index $L0] e1 [nl2 index $L1] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                lset L 0 {*}$i [list $e0 {} $e1]
                ::set i [linsert $i 0 1]
            }
            return {*}$L
        } elseif {[nl2 is left $L1] && [nl2 is right $L0]} {
            ::set L [list {}]; ::set i {}
            foreach e0 [nl2 index $L1] e1 [nl2 index $L0] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                lset L 0 {*}$i [list $e0 {} $e1]
                ::set i [linsert $i 0 1]
            }
            return {*}$L
        }
    }
    proc merge-dict {L0 L1} {
        if {[nl2 is left $L0] && [nl2 is right $L1]} {
            foreach e0 [nl2 index $L0] e1 [nl2 index $L1] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                ::lappend L $e0 $e1
            }
            return $L
        } elseif {[nl2 is left $L1] && [nl2 is right $L0]} {
            foreach e0 [nl2 index $L0] e1 [nl2 index $L1] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                ::lappend L $e0 $e1
            }
            return $L
        }
    }
    proc merge-left {L0 L1} {
        if {[nl2 is left $L0] && [nl2 is right $L1]} {
            ::set L [list {}]; ::set i {}
            foreach e0 [nl2 index $L0] e1 [nl2 index $L1] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                lset L 0 {*}$i [list {} $e0 $e1]
                ::set i [linsert $i 0 0]
            }
            return {*}$L
        } elseif {[nl2 is left $L1] && [nl2 is right $L0]} {
            ::set L [list {}]; ::set i {}
            foreach e0 [nl2 index $L1] e1 [nl2 index $L0] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                lset L 0 {*}$i [list {} $e0 $e1]
                ::set i [linsert $i 0 0]
            }
            return {*}$L
        }
    }

    proc merge-right {L0 L1} {
        if {[nl2 is left $L0] && [nl2 is right $L1]} {
            ::set L [list {}]; ::set i {}
            foreach e0 [nl2 index $L0] e1 [nl2 index $L1] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                lset L 0 {*}$i [list $e0 $e1 {}]
                ::set i [linsert $i 0 2]
            }
            return {*}$L
        } elseif {[nl2 is left $L1] && [nl2 is right $L0]} {
            ::set L [list {}]; ::set i {}
            foreach e0 [nl2 index $L1] e1 [nl2 index $L0] {
                if {[llength $e0]  !=1} {::set e0 [list $e0]}
                if {[llength $e1]  !=1} {::set e1 [list $e1]}
                lset L 0 {*}$i [list $e0 $e1 {}]
                ::set i [linsert $i 0 2]
            }
            return {*}$L
        }
    }

    proc range {L debut fin} {
        # nl2 range ...
        if {[nl2 is left $L]} {
                ::return [nl2 left {*}[::lrange [nl2 flat $L] $debut $fin]]
        } elseif {[nl2 is right $L]} {
                ::return [nl2 right {*}[::lrange [nl2 flat $L] $debut $fin]]
        }   
    }
    namespace eval repeat {
        proc left {count element args} {
            # nl2 left repeat ...
            ::return [nl2 left {*}[lrepeat $count $element {*}$args]]
        }
        proc right {count element args} {
            # nl2 left repeat ...
            ::return [nl2 right {*}[lrepeat $count $element {*}$args]]
        }
        namespace export *
        namespace ensemble create
    }
    proc reverse {L} {
        # nl2 reverse ...
        if {[nl2 is left $L]} {
            return [nl2 left {*}[lreverse [nl2 flat $L]]]
        } elseif {[nl2 is right $L]} {
            return [nl2 right {*}[lreverse [nl2 flat $L]]]
        }
    }
    proc right {args} {
        # nl2 right ...
        ::set L [::list ""]
        ::set i 0
        ::foreach e $args {
            if {[::llength $e] != 1} {::set e [::list $e]}
            ::lset L {*}$i [::list $e ""]
            ::lappend i 1
        }
        ::return {*}$L 
    }
    proc rindice {type} {
        switch -- $type {
            left {return 0}
            right {return 1}
            default {return ""}
        }
    }
    proc search {args} {
        # nl2 search ...
        ::set options [lassign [lreverse $args] pattern L]
        if {"-inline" in $options} {
                if {[nl2 is left $L]} {
                        ::return [nl2 left {*}[::lsearch {*}$options [nl2 flat $L] $pattern]]
                } elseif {[nl2 is right $L]} {
                        ::return [nl2 right {*}[::lsearch {*}$options [nl2 flat $L] $pattern]]
                }
        } else {
                ::return [::lsearch {*}$options [nl2 flat $L] $pattern]
        }
    }
    proc sort {args} {
        # nl2 sort ...
        ::set options [lassign [lreverse $args] L]
        if {[nl2 is left $L]} {
                ::return [nl2 left {*}[lsort {*}$options [nl2 flat $L]]]
        } elseif {[nl2 is right $L]} {
                ::return [nl2 right {*}[lsort {*}$options [nl2 flat $L]]]
        }
    }
    proc set {args} {
        # nl2 set ...
        ::set args [lassign $args L]
        upvar $L nl
        ::set index [lassign [lreverse $args] newValue]
        if {[nl2 is left $nl]} {
                ::set nl [nl2 flat $nl]
                ::return [::set nl [nl2 left {*}[lset nl {*}$index $newValue]]]
        } elseif {[nl2 is right $nl]} {
                ::set nl [nl2 flat $nl]
                ::return [::set nl [nl2 right {*}[lset nl {*}$index $newValue]]]
        }
    }
   proc transpose {type L} {
        # nl2 transpose ...   
       return [nl2 $type {*}[nl2 flat $L]]
   }
    proc type {L} {
        if {[nl2 is right $L]} {
            return "right"
        } elseif {[nl2 is left $L]} {
            return "left"
        }
    }
    proc !type {L} {
        if {[nl2 is right $L]} {
            return "left"
        } elseif {[nl2 is left $L]} {
            return "right"
        }
    }
    namespace export *
    namespace ensemble create
}

package provide nl2 0.1

if 0 {

Explanation and Examples of nl2 lists



The nl2 package has an interface that is close to that of list, making it so easy to remember, with some extra functionality added. There is indoubtly some bugs, please tell me.

Let's test it :

}

console show

puts [set Left [nl2 left A B C]]
# {{{} C} B} A
puts [nl2 is left $Left]
# A
puts [nl2 type $Left]
# left
puts [nl2 !type $Left]
# right
puts "index 0 : [nl2 index $Left 0], index 1 : [nl2 index $Left 1], index 2 : [nl2 index $Left 2], index all : [nl2 index $Left]"
# index 0 : A, index 1 : B, index C : 3, index all : A B C

puts [set Middle [nl2 merge $Left $Right]]
# gives
# {1 {2 {3 {} C} B} A} 

if 0 {

i.e. a purely nested list of 3 constant length.

How to access such list ? Let's introduce a proc which give all valid indices of a list

}

proc spectre {L {indice 0}} {
    set i 0
    set Spectre [list]

    set Map {
        {L i} {
            foreach a $L {
                lappend res [linsert $a 0 $i]
            }
            set res
        }
    }

    if {[llength $L] > 0} {
        foreach a $L {
            if {[llength $a] > 1} {
                lappend Spectre {*}[::apply $Map [spectre $a $indice] $i]
            } else {
                lappend Spectre [list $i]
            }
            incr i
        }
    }
    return $Spectre
}

if 0 {

We have :

}

spectre $Left 
# == {0 0 0} {0 0 1} {0 1} 1
spectre $Right
# == 0 {1 0} {1 1 0} {1 1 1}
spectre $Middle
# == 0 {1 0} {1 1 0} {1 1 1} {1 1 2} {1 2} 2

if 0 {

So, the first element of a 2-length left-nested list is at index 1. the next element is found in inserting a 0 in the index list of the current element

The first element of a 2-length right-nested list is at index 0. the next element is found in inserting a 1 in the index list of the current element

For 3-length middle-nested list, the first element is at index 0, the second is at index 2. the next element is found :

  • if the end member of the index list of the element is 2, then insert a 1 before the index list of the current element and change the last member of the index list by 0.
  • if the end member of the index list of the is 0, then change the last member of the index list by 2.

Nested list seen as Pseudo-type



Given the lists Left and Right, as used just above :

}

puts [set Left [nl2 left A B C]]
# {{{} C} B} A
puts [set Right [nl2 right A B C]]
# A {B {C {}}}
puts [expr {$Left eq $Right}]
# 0

if 0 {

The last command shows the more interesting property : even with the same data, even sorted, Left nested lists are always different from Right nested list and that can be tested. It's like a basic type, since extra information is encoded in the structure. Each kind of purely nested list could be seen as a different type, for instance

To illustrate that, let's imagine a proc which is use to configure a widget.

}

interp alias {} isOptions {} nl2 is left
interp alias {} isPack {} nl2 is right


proc confwidget {args} {
   foreach l $args {
      if {[isOptions $l]} {
         puts "widget configure {*}[nl2 index $l]"
      } elseif {[isPack $l]} {
         puts "pack configure widget {*}[nl2 index $l]"
      } else {  
         foreach {e0 e1} $l { 
            puts "bind widget $e0 $e1"
         }
      }
   }
}

set Option [nl2 left -bg red -borderwidth 2 -relief flat -text hello]
# {{{{{{{{} hello} -text} flat} -relief} 2} -borderwidth} red} -bg
set Pack [nl2 right -after Other -side left -expand 1 -fill both]
# -after {Other {-side {left {-expand {1 {-fill {both {}}}}}}}}
set Bind [dict create <Button-1> {script1} <Button-2> {script2}]
# <Button-1> script1 <Button-2> script2

confwidget $Option

# widget configure {*}-bg red -borderwidth 2 -relief flat -text hello

confwidget $Pack

# pack configure widget {*}-after Other -side left -expand 1 -fill both

confwidget $Bind

# bind widget <Button-1> script1
# bind widget <Button-2> script2

# or, doing with all kind :

confwidget $Pack $Option $Bind

# pack configure widget {*}-after Other -side left -expand 1 -fill both
# widget configure {*}-bg red -borderwidth 2 -relief flat -text hello
# bind widget <Button-1> script1
# bind widget <Button-2> script2

# in another order :

confwidget $Option $Pack $Bind

# widget configure {*}-bg red -borderwidth 2 -relief flat -text hello
# pack configure widget {*}-after Other -side left -expand 1 -fill both
# bind widget <Button-1> script1
# bind widget <Button-2> script2

if 0 {

Temporary conclusion : purely nested lists can be used as a pseudo-type. That's not a big suprise. A C-struct can be easily indexed, and also verified, since its length in memory is constant. So should it be for purely-nested list, since their llength are constant (at least if they are constructed with a {} terminator). At each level of representation, high or low, this is the regularity of the structure which helps a programmer to deal with.


Nested list of constant length 3



FM Using the same principles, here is an interface to deal with purely nested list of 3 constant llength. Such lists are of 3 kinds : I choose to name them left, right and middle. Example :

}

set L [nl3 left A B C D E F];   # ==    {{{} F E} D C} B A
set M [nl3 middle A B C D E F]; # ==    A {C {E {} F} D} B
set R [nl3 right A B C D E F];  # ==    A B {C D {E F {}}}

spectre $L; # == {0 0 0} {0 0 1} {0 0 2} {0 1} {0 2} 1 2
spectre $M; # == 0 {1 0} {1 1 0} {1 1 1} {1 1 2} {1 2} 2
spectre $R; # == 0 1 {2 0} {2 1} {2 2 0} {2 2 1} {2 2 2}

if 0 {

nl3 package

}

namespace eval nl3 {
    proc 2lindex {type index} {
        ::set nl nl[::set len 3]
        ::set depth [expr {($index)/($len-1)}]
        ::set reste [expr {($index)%($len-1)}]
        return [list {*}[lrepeat [expr {$depth}] [$nl rindice $type]] [lindex [$nl iorder $type] $reste]]
    }
    proc append {L args} {
        # nl3 append ...
        ::upvar $L nl
        ::if {[::set type [nl3 type $nl]] ne ""} {
            ::set l [nl3 flat $nl]
            ::set nl [nl3 $type {*}[lappend l {*}$args]]
            return $nl
        } else {
            return -error -message "bad list type"
        }     
    }

    proc assign {L varname args} {
        # nl3 assign ...
        if {[::set type [nl3 type $L]] ne ""} {
            return [nl3 $type {*}[uplevel [subst {lassign {[nl3 flat $L]} $varname $args}]]]
        }
    }    

#     proc concat {} {
#         # nl3 concat ...
#         # to do
#     }
    proc depth {L} {
        if {[::set type [nl3 type $L]] ne {}} {
            ::return [expr {[nl3 length $L]/2}]
        }
    }
    proc flat {L} {
        # nl3 flat ...
        if {[::set type [nl3 type $L]] ne {}} {
            ::for {::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 [nl3 rindice $type]]} {
                foreach j [nl3 iorder $type] {
                    ::lappend Res {*}[::lindex $L {*}$i $j]
                }
            }
            return $Res
        }
    }

    proc flat-dict {L} {
        # nl3 flat ...
        ::if {[nl3 is left $L]} {
            ::for {::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 0]} {
                dict set Res {*}[::lindex $L {*}$i 2] {*}[::lindex $L {*}$i 1]
            }
            return $Res
        } elseif {[nl3 is middle $L]} {
            ::for {::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 1]} {
                dict set Res {*}[::lindex $L {*}$i 0] {*}[::lindex $L {*}$i 2]
            }
            return $Res
        } elseif {[nl3 is right $L]} {
            ::for {::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 2]} {
                dict set Res {*}[::lindex $L {*}$i 0] {*}[::lindex $L {*}$i 1]
            }
            return $Res
        }
    }
    
    proc index {L args} {
        # nl3 index ...
        ::return [::lindex [nl3 flat $L] $args]         
    }

    proc insert {L index element args} {
        if {[::set type [nl3 type $L]] ne ""} {
            ::return [nl3 $type {*}[linsert [nl3 flat $L] $index $element {*}$args]]
        }
    }
    proc iorder {type} {
        # nl3 iorder ...
        switch -- $type {
            left {return [list 2 1]}
            middle {return [list 0 2]}
            right {return [list 0 1]}
            default {return ""}
        }   
    }

    namespace eval is {
        proc left {L} {
            # nl3 is left ...
            ::set res 1
            ::if {[::llength $L] == 3 && [::llength [::lindex $L 0]] == 0} {
                ::set res 1
            } elseif {[::llength $L] == 3 && [::llength [::lindex $L 0]] == 3} {
                ::set res [nl3 is left [::lindex $L 0]]
            } else {
                ::set res 0
            }
            ::set res              
        }

        proc middle {L} {
            # nl3 is middle ...
            ::set res 1
            ::if {[::llength $L] == 3 && [::llength [::lindex $L 1]] == 0} {
                ::set res 1
            } elseif {[::llength $L] == 3 && [::llength [::lindex $L 1]] == 3} {
                ::set res [nl3 is middle [::lindex $L 1]]
            } else {
                ::set res 0
            }
            ::set res  
        }

        proc right {L} {
            # nl3 is right ...           
            ::set res 1
            ::if {[::llength $L] == 3 && [::llength [::lindex $L 2]] == 0} {
                ::set res 1
            } elseif {[::llength $L] == 3 && [::llength [::lindex $L 2]] == 3} {
                ::set res [nl3 is right [::lindex $L 2]]
            } else {
                ::set res 0
            }
            ::set res   
        }
        namespace export *
        namespace ensemble create
    }

    proc join {L {sz " "}} {
        # nl3 join ...
        ::join [nl3 flat $L] $sz
    }

    proc left {args} {
        # nl3 left ...
        ::set L [::list ""]
        ::set i 0        
        ::set Reste [expr [llength $args]%2]
        ::foreach {e0 e1} [lrange $args 0 end-$Reste] {
            if {[::llength $e0]!=1} {::set e0 [list $e0]}
            if {[::llength $e1]!=1} {::set e1 [list $e1]}
            ::lset L {*}$i [::list "" $e1 $e0]
            ::lappend i 0
        }
        if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1
            if {[::llength $e0]>1} {::set e0 [list $e0]}
            if {[::llength $e1]>1} {::set e1 [list $e1]}
            ::lset L {*}$i [::list "" $e1 $e0]
        }
        ::return {*}$L
    }

    proc length {L} {
        # nl3 length ...
        if {[::set type [nl3 type $L]] ne {}} {
            ::return [::llength [nl3 flat $L]]
        }        
    }
    proc merge {L0 L1} {
        # nl3 merge ...
        if {([::set type0 [nl3 type $L0]] ne {}) && ([::set type1 [nl3 type $L1]] ne {})} {
            ::set L [::list ""]
            ::set i 0
            foreach {e00 e01}  [nl3 flat $L0] {e10 e11} [nl3 flat $L1] {
                if {[::llength $e00]!=1} {::set e00 [list $e00]}
                if {[::llength $e01]!=1} {::set e01 [list $e01]}
                if {[::llength $e10]!=1} {::set e10 [list $e10]}
                if {[::llength $e11]!=1} {::set e11 [list $e11]}
                
                lappend TempList $e00 $e01 $e11 $e10
                ::set TempList [linsert $TempList [::set r [expr {[nl3 rindice $type0]+[nl3 rindice $type1]}]] ""]
                
                ::lset L {*}$i $TempList

                unset TempList
                lappend i $r
            }
            return {*}$L
        }
    }

    proc merge-dict {} {
        # nl3 merge-dict ...
        # source ./nl3/merge-dict/id.tcl
        
    }

    proc middle {args} {
        # nl3 middle ...       
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%2]
        ::foreach {e0 e1} [lrange $args 0 end-$Reste] {
            if {[::llength $e0]!=1} {::set e0 [list $e0]}
            if {[::llength $e1]!=1} {::set e1 [list $e1]}
            ::lset L {*}$i [::list $e0 "" $e1]
            ::lappend i 1
        }
        if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1
            if {[::llength $e0]>1} {::set e0 [list $e0]}
            if {[::llength $e1]>1} {::set e1 [list $e1]}
            ::lset L {*}$i [::list $e0 "" $e1]
        }
        ::return {*}$L  
    }

    proc range {L debut fin} {
        # nl3 range ...
        if {[::set type [nl3 type $L]] ne {}} {
            ::return [nl3 $type {*}[::lrange [nl3 flat $L] $debut $fin]]
        }        
    }
    proc repeat {type count args} {
        return [nl3 $type {*}[lrepeat $count {*}$args]]
    }

    proc nindex {L nindex {index {}}} {
        if {[::set type [nl3 type $L]] ne {}} {
            if {[string match end* $nindex]} {
                if {[::set less [lindex [split $nindex -] 1]] ne {}} {
                    ::set nindex [expr {[nl3 depth $L]-$less-1}]
                } else {
                    ::set nindex [expr {[nl3 depth $L]-1}]
                }
            }
            ::for {::set I 0; ::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 [nl3 rindice $type]]} {
                if {$I == $nindex} {
                    foreach j [nl3 iorder $type] {
                        ::lappend Res {*}[::lindex $L {*}$i $j]
                    }
                    return [lindex $Res {*}$index]
                }
                incr I
            }
        }
    }
    proc reverse {L} {
        # nl3 reverse ...
        if {[::set type [nl3 type $L]] ne {}} {
            return [nl3 $type {*}[lreverse [nl3 flat $L]]]
        }
    }
    proc rindice {type} {
        switch -- $type {
            left {return 0}
            middle {return 1}
            right {return 2}
            default {return ""}
        }
    }
    proc right {args} {
        # nl3 right ...       
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%2]
        ::foreach {e0 e1} [lrange $args 0 end-$Reste] {
            if {[::llength $e0]!=1} {::set e0 [list $e0]}
            if {[::llength $e1]!=1} {::set e1 [list $e1]}
            ::lset L {*}$i [::list $e0 $e1 ""]
            ::lappend i 2
        }
        if {$Reste > 0} {
            if {[::llength $e0]>1} {::set e0 [list $e0]}
            if {[::llength $e1]>1} {::set e1 [list $e1]}
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1
            ::lset L {*}$i [::list $e0 $e1 ""]
        }
        ::return {*}$L  
    }
    proc search {args} {
        # nl3 search ...
        ::set options [lassign [lreverse $args] pattern L]
        if {[::set type [nl3 type $L]] ne {}} {
            if {"-inline" in $options} {
                ::return [nl3 $type {*}[::lsearch {*}$options [nl3 flat $L] $pattern]]
            } else {
                ::return [::lsearch {*}$options [nl3 flat $L] $pattern]
            }
        }
    }
    proc set {args} {
        # nl3 set ...        
        ::set args [lassign $args L]
        upvar $L nl
        if {[::set type [nl3 type $nl]] ne {}} {
            ::set index [lassign [lreverse $args] newValue]
            ::set nl [nl3 flat $nl]
            ::return [::set nl [nl3 $type {*}[lset nl {*}$index $newValue]]]
        }
    }

    proc sort {args} {
        # nl3 sort ...
        ::set options [lassign [lreverse $args] L]
        if {[::set type [nl3 type $L]] ne {}} {
            ::return [nl3 $type {*}[lsort {*}$options [nl3 flat $L]]]
        }        
    }

    proc transpose {type L} {
        # nl3 transpose ...
        ::return [nl3 $type {*}[nl3 flat $L]]
    }
    proc type {L} {
        # nl3 type ...
        if {[nl3 is left $L]} {
            return "left"
        } elseif {[nl3 is middle $L]} {
            return "middle"
        } elseif {[nl3 is right $L]} {
            return "right"
        } else {
            return
        }
    }
    namespace export *
    namespace ensemble create
}
package provide nl3 0.1

if 0 {

Explanation and Examples of nl3 lists

for a long application, see Trees as nested lists


Nested list of constant length 4 (nl4 package)



FM Using the same principles, here is an interface to deal with purely nested list of 4 constant llength. Such lists are of 4 kinds : I choose to name them east, north, south, west.

nl4 package

}

namespace eval nl4 {
    proc 2lindex {type index} {
        ::set nl nl[::set len 4]
        ::set depth [expr {($index)/($len-1)}]
        ::set reste [expr {($index)%($len-1)}]
        return [list {*}[lrepeat [expr {$depth}] [$nl rindice $type]] [lindex [$nl iorder $type] $reste]]
    }
    proc append {L args} {
        # nl4 append ...
        ::upvar $L nl
        ::if {[::set type [nl4 type $nl]] ne ""} {
            ::set l [nl4 flat $nl]
            ::set nl [nl4 $type {*}[lappend l {*}$args]]
            return $nl
        } else {
            return -error -message "bad list type"
        }   
    }
    proc assign {L varname args} {
        # nl4 assign ...
        if {[::set type [nl4 type $L]] ne ""} {
            return [nl4 $type {*}[uplevel [subst {lassign {[nl4 flat $L]} $varname $args}]]]
        }
    }
#     proc concat {} {
#         # nl4 concat ...
#         # source ./nl4/concat/id.tcl      
#     }
    proc depth {L} {
        if {[::set type [nl4 type $L]] ne {}} {
            ::return [expr {[nl4 length $L]/3}]
        }        
    }

    proc east {args} {
        # nl4 east ...
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%3]        
        ::foreach {e0 e1 e2} [lrange $args 0 end-$Reste] {
                if {[llength $e0] !=1} {::set e0 [list $e0]}
                if {[llength $e1] !=1} {::set e1 [list $e1]}
                if {[llength $e2] !=1} {::set e2 [list $e2]}
                ::lset L {*}$i [::list $e0 $e1 $e2 ""]
                ::lappend i 3
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            ::lset L {*}$i [::list $e0 $e1 $e2 ""]
        }
        ::return {*}$L        
    }
    proc flat {L} {
        # nl4 flat ...
        if {[::set type [nl4 type $L]] ne {}} {
            ::for {::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 [nl4 rindice $type]]} {
                foreach j [nl4 iorder $type] {
                    ::lappend Res {*}[::lindex $L {*}$i $j]
                }
            }
            return $Res
        }
    }
       
    proc index {L args} {
        # nl4 index ...
        ::return [::lindex [nl4 flat $L] $args] 
    }

    proc iorder {type} {
        switch -- $type {
            east {return [list 0 1 2]}
            north {return [list 3 2 0]}
            south {return [list 0 1 3]}
            west {return [list 3 2 1]}
            default {return ""}
        }
    }
        
    proc insert {L index element args} {
        # nl4 insert ...
        if {[::set type [nl4 type $L]] ne ""} {
            ::return [nl4 $type {*}[linsert [nl4 flat $L] $index $element {*}$args]]
        }
    }

    namespace eval is {
        
        proc east {L} {
            # nl4 is east ...
            ::set res 1
            ::if {[::llength $L] == 4 && [::llength [::lindex $L 3]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 4 && [::llength [::lindex $L 3]] == 4} {
                    ::set res [nl4 is east [::lindex $L 3]]
            } else {
                    ::set res 0
            }
            ::set res
        }
        proc north {L} {
            # nl4 is north ...
            ::set res 1
            ::if {[::llength $L] == 4 && [::llength [::lindex $L 1]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 4 && [::llength [::lindex $L 1]] == 4} {
                    ::set res [nl4 is north [::lindex $L 1]]
            } else {
                    ::set res 0
            }
            ::set res  
        }
        proc south {L} {
            # nl4 is south ...
            ::set res 1
            ::if {[::llength $L] == 4 && [::llength [::lindex $L 2]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 4 && [::llength [::lindex $L 2]] == 4} {
                    ::set res [nl4 is south [::lindex $L 2]]
            } else {
                    ::set res 0
            }
            ::set res  
        }
        proc west {L} {
            # nl4 is west ...            
            ::set res 1
            ::if {[::llength $L] == 4 && [::llength [::lindex $L 0]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 4 && [::llength [::lindex $L 0]] == 4} {
                    ::set res [nl4 is west [::lindex $L 0]]
            } else {
                    ::set res 0
            }
            ::set res              
        }
        namespace export *
        namespace ensemble create
    }
    proc join {L {sz ""}} {
        # nl4 join ...
        return [::join [nl4 flat $L] $sz]
    }

    proc length {L} {
        # nl4 length ...
        if {[::set type [nl4 type $L]] ne {}} {
            ::return [::llength [nl4 flat $L]]
        }
        
    }
    proc merge {L0 L1} {
        # nl4 merge ...
        if {([::set type0 [nl4 type $L0]] ne {}) && ([::set type1 [nl4 type $L1]] ne {})} {
            ::set L [::list ""]
            ::set i 0
            foreach {e00 e01 e02}  [nl4 flat $L0] {e10 e11 e12} [nl4 flat $L1] {
                if {[::llength $e00]!=1} {::set e00 [list $e00]}
                if {[::llength $e01]!=1} {::set e01 [list $e01]}
                if {[::llength $e02]!=1} {::set e02 [list $e02]}
                if {[::llength $e10]!=1} {::set e10 [list $e10]}
                if {[::llength $e11]!=1} {::set e11 [list $e11]}
                if {[::llength $e12]!=1} {::set e12 [list $e12]}
                
                lappend TempList $e00 $e01 $e02 $e12 $e11 $e10
                ::set TempList [linsert $TempList [::set r [expr {[nl4 rindice $type0]+[nl4 rindice $type1]}]] ""]
                
                ::lset L {*}$i $TempList
                lappend i $r
                unset TempList
            }
            return {*}$L
        }
    }

    proc nindex {L nindex {index {}}} {
        if {[::set type [nl4 type $L]] ne {}} {
            if {[string match end* $nindex]} {
                if {[::set less [lindex [split $nindex -] 1]] ne {}} {
                    ::set nindex [expr {[nl4 depth $L]-$less-1}]
                } else {
                    ::set nindex [expr {[nl4 depth $L]-1}]
                }
            }
            ::for {::set I 0; ::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 [nl4 rindice $type]]} {
                if {$I == $nindex} {
                    foreach j [nl4 iorder $type] {
                        ::lappend Res {*}[::lindex $L {*}$i $j]
                    }
                    return [lindex $Res {*}$index]
                }
                incr I
            }
        }
    }
    proc north {args} {
        # nl4 north ...
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%3]
        ::foreach {e0 e1 e2} [lrange $args 0 end-$Reste] {
                if {[llength $e0] !=1} {::set e0 [list $e0]}
                if {[llength $e1] !=1} {::set e1 [list $e1]}
                if {[llength $e2] !=1} {::set e2 [list $e2]}
                ::lset L {*}$i [::list $e2 "" $e1 $e0]
                ::lappend i 1
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            ::lset L {*}$i [::list $e2 "" $e1 $e0]
        }
        ::return {*}$L
    }
    proc range {L debut fin} {
        # nl4 range ...
        if {[::set type [nl4 type $L]] ne {}} {
            ::return [nl4 $type {*}[::lrange [nl4 flat $L] $debut $fin]]
        }
    }
    proc repeat {type count args} {
        return [nl4 $type {*}[lrepeat $count {*}$args]]
    }
    proc reverse {L} {
        # nl4 reverse ...
        # source ./nl4/reverse/id.tcl
        if {[::set type [nl4 type $L]] ne {}} {
            return [nl3 $type {*}[lreverse [nl4 flat $L]]]
        }
    }

    proc rindice {type} {
        switch -- $type {
            east {return 3}
            north {return 1}
            south {return 2}
            west {return 0}
            default {return ""}
        }
    }

    proc search {args} {
        # nl4 search ...
        ::set options [lassign [lreverse $args] pattern L]
        if {[::set type [nl4 type $L]] ne {}} {
            if {"-around" in $options} {
                ::set options [lsearch -inline -not -all $options -around]
                ::set allaround 1
            } else {
                ::set allaround 0
            }
            if {"-lindex" ni $options} {
                if {"-inline" in $options} {
                    ::set options [lsearch -inline -not -all $options -inl*]
                    ::set Indices [::lsearch {*}$options [nl4 flat $L] $pattern]
                    ::set K [list]
                    foreach i $Indices {
                        if {$allaround} {
                            ::set nIndex [expr {$i/3}]
                        } else {
                            ::set nIndex [list [expr {$i/3}] [expr {$i%3}]]
                        }
                        lappend K {*}[nl4 nindex $L {*}$nIndex]
                    }
                    if {$allaround} {
                        return [nl4 $type {*}$K]
                    } else {
                        return [nl2 right {*}$K]
                    }
                } else {
                    ::set Indices [::lsearch {*}$options [nl4 flat $L] $pattern]
                    ::set nIndex [list]
                    foreach i $Indices {
                        if {$allaround} {
                            ::set nIndex [expr {$i/3}]
                        } else {
                            ::lappend nIndex [list [expr {$i/3}] [expr {$i%3}]]
                        }
                    }
                    return $nIndex
                }
            } else {
                if {$allaround} {
                    return -error "option -around and -lindex not allowed together"
                }
                ::set options [lsearch -inline -not -all $options -lindex]
                 return [::lsearch {*}$options [nl4 flat $L] $pattern]
            }
        }
    }
    proc set {args} {
        # nl4 set ...
        ::set args [lassign $args L]
        upvar $L nl
        if {[::set type [nl4 type $nl]] ne {}} {
            ::set index [lassign [lreverse $args] newValue]
            ::set nl [nl4 flat $nl]
            ::return [::set nl [nl4 $type {*}[lset nl {*}$index $newValue]]]
        }
    }

    proc sort {args} {
        # nl4 sort ...
        ::set options [lassign [lreverse $args] L]
        if {[::set type [nl4 type $L]] ne {}} {
            ::return [nl4 $type {*}[lsort {*}$options [nl4 flat $L]]]
        }
    }

    proc south {args} {
        # nl4 south ...
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%3]        
        ::foreach {e0 e1 e2} [lrange $args 0 end-$Reste] {
                if {[llength $e0] !=1} {::set e0 [list $e0]}
                if {[llength $e1] !=1} {::set e1 [list $e1]}
                if {[llength $e2] !=1} {::set e2 [list $e2]}
                ::lset L {*}$i [::list $e0 $e1 "" $e2]
                ::lappend i 2
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            ::lset L {*}$i [::list $e0 $e1 "" $e2]
        }
        ::return {*}$L        
    }

    proc transpose {type L} {
        # nl4 transpose ...   
        ::return [nl4 $type {*}[nl4 flat $L]]
    }

    proc type {L} {
        # nl4 type ...
        if {[nl4 is east $L]} {
                return "east"
        } elseif {[nl4 is north $L]} {
                return "north"
        } elseif {[nl4 is south $L]} {
                return "south"
        } elseif {[nl4 is west $L]} {
                return "west"
        } else {
            return ""
        }
    }

    proc west {args} {
        # nl4 west ...
        ::set L [::list ""]
        ::set i 0 
        ::set Reste [expr [llength $args]%3]
        ::foreach {e0 e1 e2} [lrange $args 0 end-$Reste] {
                if {[llength $e0] !=1} {::set e0 [list $e0]}
                if {[llength $e1] !=1} {::set e1 [list $e1]}
                if {[llength $e2] !=1} {::set e2 [list $e2]}
                ::lset L {*}$i [::list "" $e2 $e1 $e0]
                ::lappend i 0
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            ::lset L {*}$i [::list "" $e2 $e1 $e0]
        }
        ::return {*}$L
        
    }

    namespace export *
    namespace ensemble create
}
package provide nl4 0.1

if 0 {

Explanation and Examples of nl4 lists



For an application, see Menu as trees as nested list


Nested list of constant length 5 (nl5 package)



FM Using the same principles, here is an interface to deal with purely nested list of 5 constant llength. Such lists are of 5 kinds : I choose to name them east, north, center, south, west.

nl5 package

}

namespace eval nl5 {
    proc 2lindex {type index} {
        ::set nl nl[::set len 5]
        ::set depth [expr {($index)/($len-1)}]
        ::set reste [expr {($index)%($len-1)}]
        return [list {*}[lrepeat [expr {$depth}] [$nl rindice $type]] [lindex [$nl iorder $type] $reste]]
    }
    proc append {L args} {
        # nl5 append ...
        ::upvar $L nl
        ::if {[::set type [nl5 type $nl]] ne ""} {
            ::set l [nl5 flat $nl]
            ::set nl [nl5 $type {*}[lappend l {*}$args]]
            return $nl
        } else {
            return -error -message "bad list type"
        }   
    }
    proc assign {L varname args} {
        # nl5 assign ...
        if {[::set type [nl5 type $L]] ne ""} {
            return [nl5 $type {*}[uplevel [subst {lassign {[nl5 flat $L]} $varname $args}]]]
        }
    }
    proc center {args} {
        # nl5 center ...
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%4]
        ::foreach {e0 e1 e2 e3} [lrange $args 0 end-$Reste] {
            if {[llength $e0] !=1} {::set e0 [list $e0]}
            if {[llength $e1] !=1} {::set e1 [list $e1]}
            if {[llength $e2] !=1} {::set e2 [list $e2]}
            if {[llength $e3] !=1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e0 $e1 "" $e3 $e2]
            ::lappend i 2
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2 e3
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            if {[llength $e3]>1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e0 $e1 "" $e3 $e2]
        }
        ::return {*}$L
    }
    proc concat {args} {
        # nl5 concat ...
        # source ./nl5/concat/id.tcl        
    }
    proc depth {L} {
        if {[::set type [nl5 type $L]] ne {}} {
            ::return [expr {[nl5 length $L]/4}]
        }
    }
    proc east {args} {
        # nl5 east ...
        ::set L [::list ""]
        ::set i 0 
        ::set Reste [expr [llength $args]%4]
        ::foreach {e0 e1 e2 e3} [lrange $args 0 end-$Reste] {
            if {[llength $e0] !=1} {::set e0 [list $e0]}
            if {[llength $e1] !=1} {::set e1 [list $e1]}
            if {[llength $e2] !=1} {::set e2 [list $e2]}
            if {[llength $e3] !=1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e0 $e1 $e2 $e3 ""]
            ::lappend i 4
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2 e3
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            if {[llength $e3]>1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e0 $e1 $e2 $e3 ""]
        }
        ::return {*}$L
    }
    proc flat {L} {
        # nl5 flat ...
        if {[::set type [nl5 type $L]] ne {}} {
            ::for {::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 [nl5 rindice $type]]} {
                foreach j [nl5 iorder $type] {
                    # A voir.
                    if {[llength [::lindex $L {*}$i $j]] > 1} {
                        ::lappend Res [::lindex $L {*}$i $j]
                    } else {
                        ::lappend Res {*}[::lindex $L {*}$i $j]
                    }
                }
            }
            return $Res
        }
    }
    proc index {L args} {
        # nl5 index ...
        ::return [::lindex [nl5 flat $L] $args] 
    }
    proc insert {L index element args} {
        # nl5 insert ...
        if {[::set type [nl5 type $L]] ne ""} {
            ::return [nl5 $type {*}[linsert [nl5 flat $L] $index $element {*}$args]]
        }
    }
    proc iorder {type} {
        # nl5 iorder ...
        switch -- $type {
            center {return [list 0 1 4 3]}
            east {return [list 0 1 2 3]}
            north {return [list 4 3 2 0]}
            south {return [list 0 1 2 4]}
            west {return [list 4 3 2 1]}
            default {return ""}
        }   
    }
    namespace eval is {
    
        proc center {L} {
            # nl5 is center ...
            ::set res 1
            ::if {[::llength $L] == 5 && [::llength [::lindex $L 2]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 5 && [::llength [::lindex $L 2]] == 5} {
                    ::set res [nl5 is center [::lindex $L 2]]
            } else {
                    ::set res 0
            }
            return $res
        }
        proc east {L} {
            # nl5 is east ...
            ::set res 1
            ::if {[::llength $L] == 5 && [::llength [::lindex $L 4]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 5 && [::llength [::lindex $L 4]] == 5} {
                    ::set res [nl5 is east [::lindex $L 4]]
            } else {
                    ::set res 0
            }
            return $res
        }
        proc north {L} {
            # nl5 is north ...
            ::set res 1
            ::if {[::llength $L] == 5 && [::llength [::lindex $L 1]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 5 && [::llength [::lindex $L 1]] == 5} {
                    ::set res [nl5 is north [::lindex $L 1]]
            } else {
                    ::set res 0
            }
            return $res
        }
        proc south {L} {
            # nl5 is south ...
            ::set res 1
            ::if {[::llength $L] == 5 && [::llength [::lindex $L 3]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 5 && [::llength [::lindex $L 3]] == 5} {
                    ::set res [nl5 is south [::lindex $L 3]]
            } else {
                    ::set res 0
            }
            return $res
        }
        proc west {L} {
            # nl5 is west ...
            ::set res 1
            ::if {[::llength $L] == 5 && [::llength [::lindex $L 0]] == 0} {
                    ::set res 1
            } elseif {[::llength $L] == 5 && [::llength [::lindex $L 0]] == 5} {
                    ::set res [nl5 is west [::lindex $L 0]]
            } else {
                    ::set res 0
            }
            return $res
        }
        namespace export *
        namespace ensemble create
    }
    proc join {L {sz " "}} {
        # nl5 join ...
        return [::join [nl5 flat $L] $sz]
    }
    proc length {L} {
        # nl5 length ...
        if {[::set type [nl5 type $L]] ne {}} {
            ::return [::llength [nl5 flat $L]]
        }
        # source ./nl5/length/id.tcl
        
    }
    proc merge {L0 L1} {
        # nl4 merge ...
        if {([::set type0 [nl5 type $L0]] ne {}) && ([::set type1 [nl5 type $L1]] ne {})} {
            ::set L [::list ""]
            ::set i 0
            foreach {e00 e01 e02 e03}  [nl5 flat $L0] {e10 e11 e12 e13} [nl5 flat $L1] {
                if {[::llength $e00]!=1} {::set e00 [list $e00]}
                if {[::llength $e01]!=1} {::set e01 [list $e01]}
                if {[::llength $e02]!=1} {::set e02 [list $e02]}
                if {[::llength $e03]!=1} {::set e03 [list $e03]}
                if {[::llength $e10]!=1} {::set e10 [list $e10]}
                if {[::llength $e11]!=1} {::set e11 [list $e11]}
                if {[::llength $e12]!=1} {::set e12 [list $e12]}
                if {[::llength $e13]!=1} {::set e13 [list $e13]}
                
                lappend TempList $e00 $e01 $e02 $e03 $e13 $e12 $e11 $e10
                ::set TempList [linsert $TempList [::set r [expr {[nl5 rindice $type0]+[nl5 rindice $type1]}]] ""]
                
                ::lset L {*}$i $TempList
                lappend i $r
                unset TempList
            }
            return {*}$L
        }
    }
    proc nindex {L nindex {index {}}} {
        if {[::set type [nl5 type $L]] ne {}} {
            if {[string match end* $nindex]} {
                if {[::set less [lindex [split $nindex -] 1]] ne {}} {
                    ::set nindex [expr {[nl5 depth $L]-$less-1}]
                } else {
                    ::set nindex [expr {[nl5 depth $L]-1}]
                }
            }
            ::for {::set I 0; ::set i {}} {[::llength [::lindex $L {*}$i]]!=0} {::set i [::linsert $i 0 [nl5 rindice $type]]} {
                if {$I == $nindex} {
                    foreach j [nl5 iorder $type] {
                        ::lappend Res {*}[::lindex $L {*}$i $j]
                    }
                    return [lindex $Res {*}$index]
                }
                incr I
            }
        }
    }

    proc north {args} {
        # nl5 north ...
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%4]
        ::foreach {e0 e1 e2 e3} [lrange $args 0 end-$Reste] {
            if {[llength $e0] !=1} {::set e0 [list $e0]}
            if {[llength $e1] !=1} {::set e1 [list $e1]}
            if {[llength $e2] !=1} {::set e2 [list $e2]}
            if {[llength $e3] !=1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e3 "" $e2 $e1 $e0]
            ::lappend i 1
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2 e3
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            if {[llength $e3]>1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e3 "" $e2 $e1 $e0]
        }
        ::return {*}$L
    }

    proc range {L debut fin} {
        # nl5 range ...
        if {[::set type [nl5 type $L]] ne {}} {
            ::return [nl5 $type {*}[::lrange [nl5 flat $L] $debut $fin]]
        }
    }
    proc repeat {type count args} {
        ::return [nl5 $type {*}[lrepeat $count $args]]
    }
    proc reverse {L} {
        # nl5 reverse ...
        if {[::set type [nl5 type $L]] ne {}} {
            return [nl5 $type {*}[lreverse [nl5 flat $L]]]
        }
    }
    proc rindice {type} {
        # nl5 rindice ...
        switch -- $type {
            center {return 2}
            east {return 4}
            north {return 1}
            south {return 3}
            west {return 0}
            default {return ""}
        }
    }
    proc search {args} {
        # nl5 search ...
        ::set options [lassign [lreverse $args] pattern L]
        if {[::set type [nl5 type $L]] ne {}} {
            if {"-inline" in $options} {
                ::return [nl5 $type {*}[::lsearch {*}$options [nl5 flat $L] $pattern]]
            } else {
                ::return [::lsearch {*}$options [nl5 flat $L] $pattern]
            }
        }
    }
    proc set {args} {
        # nl5 set ...
        ::set args [lassign $args L]
        upvar $L nl
        if {[::set type [nl5 type $nl]] ne {}} {
            ::set index [lassign [lreverse $args] newValue]
            ::set nl [nl5 flat $nl]
            ::return [::set nl [nl5 $type {*}[lset nl {*}$index $newValue]]]
        }     
    }
    proc sort {args} {
        # nl5 sort ...
        ::set options [lassign [lreverse $args] L]
        if {[::set type [nl5 type $L]] ne {}} {
            ::return [nl5 $type {*}[lsort {*}$options [nl5 flat $L]]]
        }
    }
    proc south {args} {
        # nl5 south ...
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%4]        
        ::foreach {e0 e1 e2 e3} [lrange $args 0 end-$Reste] {
            if {[llength $e0] !=1} {::set e0 [list $e0]}
            if {[llength $e1] !=1} {::set e1 [list $e1]}
            if {[llength $e2] !=1} {::set e2 [list $e2]}
            if {[llength $e3] !=1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e0 $e1 $e2 "" $e3]
            ::lappend i 3
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2 e3
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            if {[llength $e3]>1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list $e0 $e1 $e2 "" $e3]
        }
        ::return {*}$L
    }
    proc transpose {type L} {
        # nl5 transpose ...   
        ::return [nl5 $type {*}[nl5 flat $L]]
    }
    proc type {L} {
        # nl5 type ...
        if {[nl5 is center $L]} {
            return "center"
        } elseif {[nl5 is east $L]} {
            return "east"
        } elseif {[nl5 is north $L]} {
            return "north"
        } elseif {[nl5 is south $L]} {
            return "south"
        } elseif {[nl5 is west $L]} {
            return "west"
        } else {
            return ""
        }
    }
    proc west {args} {
        # nl5 west ...
        ::set L [::list ""]
        ::set i 0
        ::set Reste [expr [llength $args]%4]                
        ::foreach {e0 e1 e2 e3} [lrange $args 0 end-$Reste] {
            if {[llength $e0] !=1} {::set e0 [list $e0]}
            if {[llength $e1] !=1} {::set e1 [list $e1]}
            if {[llength $e2] !=1} {::set e2 [list $e2]}
            if {[llength $e3] !=1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list "" $e3 $e2 $e1 $e0]
            ::lappend i 0
        }
        ::if {$Reste > 0} {
            lassign [lrange $args end-[expr {$Reste-1}] end] e0 e1 e2 e3
            if {[llength $e0]>1} {::set e0 [list $e0]}
            if {[llength $e1]>1} {::set e1 [list $e1]}
            if {[llength $e2]>1} {::set e2 [list $e2]}
            if {[llength $e3]>1} {::set e3 [list $e3]}
            ::lset L {*}$i [::list "" $e3 $e2 $e1 $e0]
        }
        ::return {*}$L
    }
    namespace export *
    namespace ensemble create
}

package provide nl5 0.1

if 0 {

Explanation and Examples of nl5 lists



for an example, see MegaWidgets as nested list


Nested list of constant length > 5 (nln package)

To do.

Knowing the llength of the list, and the nested indice (recursive indice), it should be possible generate the code when needed.

Nested lists whose llength and nested indice = f(depth)

Principle

This is the case when it's possible to deduce from the depth of a nested list, its llength and the nest indice (the indice where to find the nexts elements)

Construction procedure example :

}

proc nl% {args} {    
    # nl% ...
    ::set L [::list ""]
    ::set i 0
    ::set j 0
    ::set depth 1
    
    set Elements {{depth} {
        for {set i 1} {$i <= [::apply $::Length $depth]} {incr i} {
            lappend E e$i
        }
        return $E
    }}
    
    for {set i 0} {$i < [llength $args]} {incr depth} {
        lassign [lrange $args $i [incr i [::apply $::Length $depth]]] {*}[set E [::apply $Elements $depth]]
        foreach e $E {
            if {[::llength $e] != 1} {
                lappend EE [list [subst \$$e]]
            } else {
                lappend EE [subst \$$e]
            }
        }
        set EE [linsert $EE [::apply $::Nindice $depth] ""]
        ::lset L {*}$j $EE
        ::lappend j [::apply $::Nindice $depth]
        set EE [list]
        set E [list]
    }
    ::return {*}$L  
}

# some lambda to test

# left nested list of 2-constant length 
set ::Length {{depth} {
    return 1
}}

set ::Nindice {{depth} {
    return 0
}}

nl% A B C D E F G H I J K L M N O
# {{{{{{{{{{{{{{{} O} N} M} L} K} J} I} H} G} F} E} D} C} B} A
nl2 left A B C D E F G H I J K L M N O
# {{{{{{{{{{{{{{{} O} N} M} L} K} J} I} H} G} F} E} D} C} B} A

# llength = depth²
set ::Length {{depth} {
    return [expr (entier(pow(2,$depth)))]
}}

set ::Nindice {{depth} {
    return [expr (entier(pow(2,$depth)))/2]
}}

nl% A B C D E F G H I J K L M N 
# A {C D {G H I J {} K L M N} E F} B -> depth = 1, llength = 2; depth = 2, llength = 4; depth = 3, llength = 8, ...

# periodic
set ::Length {{depth} {
    set pi [expr {acos(-1)}]
    return [expr (entier(sin($pi/2*$depth)))+2]
}}

set ::Nindice {{depth} {
    return 1
}}

nl% A B C D E F G H I J K L M N O P 
# A {D {F {G {I {L {N {O {} P}} M} J K} H}} E} B C -> \
depth = 1, llength = 4; depth = 2, llength = 3; depth = 3, llength = 2; depth = 4, llength = 3; depth = 5, llength = 4 ; etc...

# logarithmic 
set ::Length {{depth} {
    return [expr (entier(log($depth))+1)]
}}

set ::Nindice {{depth} {
    return [expr (entier(log($depth))+1)/2]
}}
nl% A B C D E F G H I J K L M N O;# give
# {{C {E {G {I {K {M {} N O} L} J} H} F} D} B} A

# irrationnal (decimals of pi)
set ::Length {{depth} {
    set L [lsearch -all -not -inline [split [set pi [expr acos(-1)]] {}] .]
    return [lindex $L $depth-1]
}}

set ::Nindice {{depth} {
    return 1
}}

nl% A B C D E F G H I J K L M O P Q R S T U V W X
# A {D {E {I {J {P {} Q R S T U V W X} K L M O}} F G H}} B C; (80 members max)

## etc ...