Main Content

lists and struct::list in Tcl - Introduction to struct::list and examples

Archive - Originally posted on "The Horse's Mouth" - 2012-02-18 17:42:09 - Graham Ellis

In addition to the built in list commands, Tcl (since release 8.4) has been shipped with an additional struct::list package which includes additional procs (commands) you can use for manipulating lists.

::struct::list longestCommonSubsequence sequence1 sequence2 ?maxOccurs?
::struct::list longestCommonSubsequence2 sequence1 sequence2 ?maxOccurs?
::struct::list lcsInvert lcsData len1 len2
::struct::list lcsInvert2 lcs1 lcs2 len1 len2
::struct::list lcsInvertMerge lcsData len1 len2
::struct::list lcsInvertMerge2 lcs1 lcs2 len1 len2
::struct::list reverse sequence
::struct::list shuffle list
::struct::list assign sequence varname ?varname?...
::struct::list flatten ?-full? ?--? sequence
::struct::list map sequence cmdprefix
::struct::list mapfor var sequence script
::struct::list filter sequence cmdprefix
::struct::list filterfor var sequence expr
::struct::list split sequence cmdprefix ?passVar failVar?
::struct::list fold sequence initialvalue cmdprefix
::struct::list shift listvar
::struct::list iota n
::struct::list equal a b
::struct::list repeat size element1 ?element2 element3...?
::struct::list repeatn value size...
::struct::list dbJoin ?-inner|-left|-right|-full? ?-keys varname? {keycol table}...
::struct::list dbJoinKeyed ?-inner|-left|-right|-full? ?-keys varname? table...
::struct::list swap listvar i j
::struct::list firstperm list
::struct::list nextperm perm
::struct::list permutations list
::struct::list foreachperm var list body

Those commands which are show in bold above are shown in the following example. Firstly, load the package:

  package require struct::list

Set up a list, and reverse the order of the elements:

  set outbound {Swindon Chippenham Melksham Trowbridge Westbury}
  set inbound [struct::list reverse $outbound]
  puts $outbound
  puts $inbound


give you

  Swindon Chippenham Melksham Trowbridge Westbury
  Westbury Trowbridge Melksham Chippenham Swindon


Modify each member by running your own coommand (proc) on it:

  proc aok where {
    append where " on time"
    return $where
    }
  set running [struct::list map $outbound aok]
  puts $running


And here is the resulting list:

  {Swindon on time} {Chippenham on time} {Melksham on time} {Trowbridge on time} {Westbury on time}

Alternatively, you can pass each member of a list into a new type of loop - the mapfor loop. I could have also written this using foreach, mind you!

  struct::list mapfor place $inbound {
    puts -nonewline "[string toupper $place ], "
    }


And that outputs:

  WESTBURY, TROWBRIDGE, MELKSHAM, CHIPPENHAM, SWINDON,

The filter subcommand lets you select list members based on a criteria. There is also a filterfor

  proc pork raw {string match *ham $raw}
  set ham [struct::list filter $outbound pork]
  puts $ham


And that gives just

  Chippenham Melksham

I can use repeat to clone copies of one or more items into a list, and if those items themselves are lists I will end up with a list of lists:

  set rota [struct::list repeat 5 $outbound $inbound]
  puts $rota


which looks like this:

  {Swindon Chippenham Melksham Trowbridge Westbury} {Westbury Trowbridge
  Melksham Chippenham Swindon} {Swindon Chippenham Melksham Trowbridge Westbury}
  {Westbury Trowbridge Melksham Chippenham Swindon} {Swindon Chippenham
  Melksham Trowbridge Westbury} {Westbury Trowbridge Melksham Chippenham Swindon}
  {Swindon Chippenham Melksham Trowbridge Westbury} {Westbury Trowbridge
  Melksham Chippenham Swindon} {Swindon Chippenham Melksham Trowbridge Westbury}
  {Westbury Trowbridge Melksham Chippenham Swindon}


If I want it as a single list, I can then use flatten

  set asone [struct::list flatten $rota]
  puts $asone


and end up with results like this:

  Swindon Chippenham Melksham Trowbridge Westbury Westbury Trowbridge Melksham
  Chippenham Swindon Swindon Chippenham Melksham Trowbridge Westbury Westbury
  Trowbridge Melksham Chippenham Swindon Swindon Chippenham Melksham Trowbridge
  Westbury Westbury Trowbridge Melksham Chippenham Swindon Swindon Chippenham
  Melksham Trowbridge Westbury Westbury Trowbridge Melksham Chippenham Swindon
  Swindon Chippenham Melksham Trowbridge Westbury Westbury Trowbridge Melksham
  Chippenham Swindon


As an alternative, I could use the {*} (expand) notation to expand my incoming lists before I repeat them, thus saving the double step of producing a list of lists which then had to be flattened:

  set rota [struct::list repeat 5 {*}$outbound {*}$inbound]
  puts $rota


Which directly gives me:

  Swindon Chippenham Melksham Trowbridge Westbury Westbury Trowbridge Melksham
  Chippenham Swindon Swindon Chippenham Melksham Trowbridge Westbury Westbury
  Trowbridge Melksham Chippenham Swindon Swindon Chippenham Melksham Trowbridge
  Westbury Westbury Trowbridge Melksham Chippenham Swindon Swindon Chippenham
  Melksham Trowbridge Westbury Westbury Trowbridge Melksham Chippenham Swindon
  Swindon Chippenham Melksham Trowbridge Westbury Westbury Trowbridge Melksham
  Chippenham Swindon


Finally, here are shift to take the first element off a list, and swap to swap over two elements:

  set starts [struct::list shift outbound]
  puts "Runs from $starts to $outbound"
  struct::list swap outbound 0 3
  struct::list swap outbound 1 2
  puts "Runs back from $outbound to $starts"


And that produces:

  Runs from Swindon to Chippenham Melksham Trowbridge Westbury
  Runs back from Westbury Trowbridge Melksham Chippenham to Swindon


struct::list also supports database-like joining, tools for producing all permultations of a list, a randomiser to shuffle a list, and a series of lcs or longestCommonSequence commands which are designed for data comparison where you're looking for differences.

Full source of the above is [here]. We'll introduce you to lists, and struct::list too, on our Tcl, Tk and Expect courses.