This is not necessarily the current version of this TIP.
| TIP: | 22 |
| Title: | Multiple Index Arguments to lindex |
| Version: | $Revision: 1.14 $ |
| Authors: |
David Cuthbert <dacut at kanga dot org> Kevin Kenny <kennykb at acm dot org> Don Porter <dgp at users dot sourceforge dot net> Donal K. Fellows <fellowsd at cs dot man dot ac dot uk> |
| State: | Draft |
| Type: | Project |
| Tcl-Version: | 8.4a2 |
| Vote: | Pending |
| Created: | Friday, 19 January 2001 |
| Discussions To: | news:comp.lang.tcl |
| Discussions To: | mailto:kennykb@acm.org |
| Keywords: | lindex, multiple arguments, sublists |
Obtaining access to elements of sublists in Tcl often requires nested calls to the lindex command. The indices are syntactically listed in most-nested to least-nested order, which is the reverse from other notations. In addition, the nesting of command substitution brackets further decreases readability. This proposal describes an extension to the lindex command that allows it to accept multiple index arguments, in least-nested to most-nested order, to automatically extract elements of sublists.
The heterogeneous nature of Tcl lists allows them to be applied to a number of useful data structures. In particular, lists can contain elements that are, themselves, valid lists. In this document, these elements are referred to as sublists.
Extracting elements from sublists often requires nested calls to lindex. Consider, for example, the following Tcl script that prints the center element of a 3-by-3 matrix:
set A {{1 2 3} {4 5 6} {7 8 9}}
puts [lindex [lindex $A 2] 2]
When these calls are deeply nested - e.g., embedded in an expr arithmetic expression, having results extracted through lrange, etc. - the results are difficult to read:
# Print the sum of the center indices of two 3x3 matrices
set p [expr {[lindex [lindex $A 2] 2] + [lindex [lindex $A 2] 2]}]
# Get all but the last font in the following parsed structure:
set pstruct {text {ignored-data
{ ... }
}
{valid-styles
{justifiction {left centered right full}}
{font {courier helvetica times}}
}
}
return [lrange [lindex [lindex [lindex $pstruct 1] 2] 2] 0 end-1]
Note that the list of indices in the latter example is listed in the reverse order of vector indices. In most other languages/domains, the last line might take on one of the following forms:
return list_range(pstruct[2][2][1], 0, end-1); return pstruct[[2, 2, 1]][[0:-1]] temp = pstruct(2, 2, 1); result = range(temp, 0, length(temp) - 1);
Allowing the lindex command to accept multiple arguments would allow this more-natural style of coding to be written in Tcl.
Allow lindex to accept an arbitrary number of arguments.
The first argument (the list argument) must be a proper Tcl list. No change is required from current behaviour.
The remaining arguments (the index arguments) must be proper list indices (either integer, end, or end-integer).
When only one index argument is given, the behaviour is unchanged from the current lindex command.
When multiple index arguments are given, the behaviour is defined recursively as:
lindex alist i0 i1 i2 ... === lindex [lindex alist i0] i1 i2 ...
Note that this does not define any restrictions on the implementation, which may be recursive or iterative.
When an invalid index is given, an error of the form, bad index "invalid_index": must be integer or end?-integer?, where invalid_index is the first invalid index encountered, must be returned.
If the list argument is malformed, the error resulting from an attempt to convert the list argument to a list must be returned. This behaviour is unchanged from the current implementation.
Whether the result of the lindex operation is successful, the underlying Tcl_Obj that represents the list argument may have its internal representation invalidated or changed to that of a list.
Don Porter <dgp at users dot sourceforge dot net>
I agree that it would be helpful to many programmers to provide a multi-dimensional array data structure that can be accessed in the manner described in this TIP. In the struct module of tcllib, several other data structures are being developed: graph, tree, queue, stack. I would support adding another data structure to that module that provides an interface like the one described in this TIP, with the intent that all of these helpful data structures find their way into the BI distribution.
I don't see any advantage to adding complexity to [lindex] as an alternative to development of a multi-dimensional array structure. Without a compelling advantage, I'm inclined against making [lindex] more complex. I like having Tcl's built-in commands provide primitive operations, and leave it to packages to combine the primitives into more useful, more complex resources.
This TIP should also consider how any changes to [lindex] mesh with the whole [listx] overhaul of Tcl's [list] command that has been discussed.
Dave Cuthbert <dacut at kanga dot org> responds
Don makes a good point -- with a good set of data structures in tcllib, the need for this TIP is lessened or even eliminated. Nonetheless, I see this as a way of implementing the structures he describes. In other words, a more powerful primitive (which, in reality, adds fairly little complexity when measured in number of lines of code changed) would benefit these structures.
As for the [listx] overhaul, there are many competing proposals for the specification it is difficult to come up with a metric. In writing this TIP, I assumed a vacuum -- that is, a listx command would not be added to the core in the near future.
Donal K. Fellows <fellowsd at cs dot man dot ac dot uk> points out
Although there is tcllib and [listx] to think about, they are certainly not reasons for rejecting this TIP out of hand. The availability of tcllib is not currently anything like universal (not stating whether this is a good, bad or ugly thing) and all the [listx] work will need its own TIP to make it into the core (you tend to have availability problems if it is an extension.) It is not as if the core is short of syntactic sugar right now (the [foreach] command is ample demonstration of this.)
There was quite a discussion on news:comp.lang.tcl about using lindex to return multiple arguments. For example:
% set list {a {b1 b2} c d e}
% lindex $list 1
b1 b2
% lindex $list 1 0
{b1 b2} a
% lindex $list {1 0}
b1
In other words, the list index arguments can, themselves, be lists. Only when the argument is a list would the "recursive selection" procedure of the TIP be used. For multiple arguments, the behaviour is akin to
lindex $list a b c ->
list [lindex $list a] [lindex $list b] [lindex $list c]
Summarised by Dave Cuthbert <dacut at kanga dot org>
Donal K. Fellows <fellowsd at cs dot man dot ac dot uk> points out
The problems with the above version of multiple indexing are that it loses the property that [lindex] always returns a single element (making writing robust code harder) and that it forces use of the [list] constructor a lot or inefficient type handling when some of the indices must be computed. Then there is the whole question of what happens when you have indexes that are lists of lists, which is a major can of worms.
Luckily, we could always put this sort of behaviour into a separate command (e.g. called [lselect]) which addresses at least the majority of my concerns, and which (in my opinion) need not even form part of this TIP.
This TIP was originally written by Dave Cuthbert <dacut at kanga dot org>, but ownership has passed (beginning of April) to Kevin Kenny <kennykb at acm dot org>.
This document has been placed in the public domain.
This is not necessarily the current version of this TIP.