Version 10 of list level

Updated 2006-10-10 19:55:51

ulis: If, for some syntax analyzis, you need to know the level of nested braces inside a list, here is a tiny proc that do that.

It returns 0 for "this is a list", 1 for "{this is a list}" or "this {is a} list", and so on.


  proc listlevel {text} { 
     set len [llength [split $text]]
     for {set count 0} {$len!=[set len [llength $text]]} {incr count} {
        # Used to be [eval concat $text] but this is safer
        set text [eval [linsert $text 0 concat]]
     }
     return $count
  }

ulis: This proc doesn't get the good result because llength $text can't differentiate between {{this is a list}} and {{{this is a list}}} (each returns 1). Applied to the test below it returns: 0 0 0 2 2 3 1. (Expected results are: 0 0 0 1 1 2 2).


ulis: Here is a proc that gets the expected results from the test below. (Maybe somebody will find a flaw?)

  proc listlevel {list} \
  {
    # initial length & count
    set len [string length $list]
    set count 0
    # while string length differs between list & concatened list
    while {[string length [set list [eval concat $list]]] != $len} \
    { 
      # update length & count
      set len [string length $list]
      incr count 
    }
    return $count
  }

Can somebody explain me why eval [linsert $list 0 concat] is better than [eval concat $list in this context?

AK -- eval is given a pure list and goes through a specially optimized path for the eval/linsert combo. Without the linsert 'concat' is no pure list and thus eval does the regular stuff, which is slower.


Results:

  set text "this is a list"
  puts "$text: [listlevel $text]"
  -> this is a list: 0

  set text {this is a list}
  puts "$text: [listlevel $text]"
  -> this is a list: 0

  set text [list this is a list]
  puts "$text: [listlevel $text]"
  -> this is a list: 0

  set text [list "this is a list"]
  puts "$text: [listlevel $text]"
  -> {this is a list}: 1

  set text {{this is a} list}
  puts "$text: [listlevel $text]"
  -> {this is a} list: 1

  set text {{this {is a}} list}
  puts "$text: [listlevel $text]"
  -> {this {is a}} list: 2

  set text {{{this is a list}}}
  puts "$text: [listlevel $text]"
  -> {{this is a list}}: 2

ulis: Tcl makes no difference between "this is a list" and [list this is a list] so I gave them the 0 level.

You can need to differentiate empty strings, one word strings and multi words strings before nested lists.


Slight problem. It doesn't like list encapsulated single element lists. Nil and multi-element lists work:

 % listlevel [list a]
 0
 % listlevel [list {a}]
 0
 % listlevel [list {}]
 1
 % listlevel [list {a b}]
 1
 %