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 EllisIn 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.