Arjen Markus (14 april 2004) Here is another experiment with sets of numerical data - in C and Fortran and many other programming languages commonly called multidimensional arrays. The operation implemented below allows you to select so-called
slices from these blocks of data. This being done in Tcl, it makes little difference if the data are indeed numbers or something else.
It is an operation that is used quite frequently to copy/select data for some computation.
# slicearray.tcl --
# Procedure to implement slicing operations on lists and
# lists of lists:
# slice { { 1 2 3 4 5}
# { 6 7 8 9 10}
# {11 12 13 14 15} } {0 1} ==> { { 1 2 3 4 5}
# { 6 7 8 9 10} }
#
# slice { { 1 2 3 4 5}
# { 6 7 8 9 10}
# {11 12 13 14 15} } {} {0 1} ==> { { 1 2}
# { 6 7}
# {11 12} }
#
# Note:
# The procedure works on any level of nesting, but it
# does assume that the indices are within range - there
# is no check whether that is indeed the case.
# arrays --
# An appropriate namespace
#
namespace eval ::arrays {
namespace export slice
}
# slice --
# Return a subset of the data using a slicing operation
# Arguments:
# data The data to be sliced (a list, list of lists, list of
# lists of lists, ...)
# indices A list of zero to three values indicating which
# elements should be retained
# args Slicing arguments for the next (deeper) level
# Result:
# New multidimensional array with a reduced number of data
#
# Note:
# The indices may be:
# - An empty list, then the whole dimension is retained
# - A single element, which means only one index is retained
# - Two elements, specifying the start and stop of the indices
# (implicit stepsize: 1)
# - Three elements: stasrt, stop and stepsize
#
proc ::arrays::slice {data indices args} {
set recursive 1
if { [llength $args] == 0 } {
set recursive 0
}
if { [llength $indices] == 0 } {
set start 0
set stop [expr {[llength $data]-1}]
set step 1
}
if { [llength $indices] == 1 } {
set start [lindex $indices 0]
set stop $start
set step 1
}
if { [llength $indices] == 2 } {
set start [lindex $indices 0]
set stop [lindex $indices 1]
set step 1
}
if { [llength $indices] == 3 } {
set start [lindex $indices 0]
set stop [lindex $indices 1]
set step [lindex $indices 2]
}
set noiter [expr {int(($stop-$start+$step)/$step)}]
set iter 0
set idx $start
set result {}
if { $recursive } {
while { $iter < $noiter } {
lappend result [eval slice [list [lindex $data $idx]] $args]
incr idx $step
incr iter
}
} else {
while { $iter < $noiter } {
lappend result [lindex $data $idx]
incr idx $step
incr iter
}
}
return $result
}
# testing the implementation
#
set data { { 1 2 3 4 5}
{ 6 7 8 9 10}
{11 12 13 14 15} }
puts [::arrays::slice $data {0 1}]
puts [::arrays::slice $data {} {0 1}]
puts [::arrays::slice $data {} {0 4 2}]
puts [::arrays::slice $data {2 1 -1} {0 4 2}]