<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE TIP SYSTEM "http://www.tcl.tk/cgi-bin/tct/tip/tipxml.dtd">
<!-- Converted at Sun Feb 12 12:07:37 GMT 2012 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='170'>
<header><title>Better Support for Nested Lists</title><author address="mailto:babkin@freebsd.org">Sergey Babkin</author><author address="mailto:dgp@users.sf.net">Don Porter</author><author address="mailto:dkf@users.sf.net">Donal K. Fellows</author><status type='project' state='draft' tclversion="8.7" vote='prior'>$Revision: 1.6 $</status><history></history><created day='30' month='jan' year='2004' /></header>
<abstract>Nested lists are easy to create with Tcl but then manipulating them is not easy. For example, think about how to change a value nested in a list 2 levels deep? How about 4 levels deep? The proposed new commands make such manipulation easy, and make the nested lists a great replacement for arrays and structures in C-like languages.</abstract>
<body><section title="Rationale and Specification">
<para>The new proposed commands start with the prefix &quot;ldeep&quot;. They are desgined to resemble the classic list commands with &quot;l&quot; names. In the cases when the meaning of &quot;ldeep&quot; command differs substantially from the &quot;l&quot; command, the name has been selected to be different (such as &quot;ldeeprep&quot;, not &quot;ldeepreplace&quot;).</para>
<para>The commands have been extensively used in the Not A Commander project [<url ref="http://nac.sf.net/"/>] and have been adjusted to the observed needs of practical use. All these commands use the concept of &quot;path&quot; (see <tipref type="text" tip="22"/>) and handle the concatenation of paths for reasons of convenience (e.g. a &quot;base&quot; path followed by a &quot;local&quot; path).</para>
<subsection title="Commands">
<para>The new proposed commands are:</para>
<quote><emph style="bold">ldeepset</emph> <emph style="italic">listvar path</emph> ?<emph style="italic">path</emph>?... <emph style="italic">value</emph></quote>
<para>Set a <emph style="italic">value</emph> in a nested list variable. If the variable did not exist previously, it is created. If the intermediate lists specified in the path did not exist, they are created as empty lists. This includes the &quot;filler&quot; elements: for example, if <emph style="italic">listvar</emph> contained a list of one element and the path starts with &quot;5 ...&quot;, the elements with indexes 1, 2, 3, 4 and 5 will all be created (and will contain empty strings) and the the further creation within the element at index 5 will proceed.</para>
<para>A special meaning is assigned by this command to the negative indexes in the path: they mean &quot;add a new element at the end of the list&quot;. So this command also doubles as a nested version of lappend. For example,</para>
<verbatim><vline encoding='base64'>ICAgbGRlZXBzZXQgbGlzdHZhciAtMSB2YWx1ZQ==</vline></verbatim>
<para>means the same thing as</para>
<verbatim><vline encoding='base64'>ICAgbGFwcGVuZCBsaXN0dmFyIHZhbHVl</vline></verbatim>
<para>This merging has happened because it&apos;s often neccessary to add elements to the lists in the middle of the path. The particular value used to indicate the addition of an element can be changed to something more symbolic, for example to &quot;append&quot; instead of a negative value.</para>
<para>The <emph style="italic">ldeepset</emph> command returns nothing. Since the version without value as in the common set can not be used, returning the value did not seem to make sense. Also when experimenting with large lists from the command line, returning the value that is a large list itself would cause a long and unpleasant printout of it.</para>
<para><emph style="italic">(This is only partially superceded by <tipref type="text" tip="33"/> and <tipref type="text" tip="331"/>.)</emph></para>
<quote><emph style="bold">ldeepincr</emph> <emph style="italic">lstvar path</emph> ?<emph style="italic">path</emph>?... <emph style="italic">int-value</emph></quote>
<para>Increase a value within a nested list by <emph style="italic">int-value</emph>. Note that since the amount of increase has to be differentiated from the <emph style="italic">path</emph>, it&apos;s mandatory even for the value of 1. This is a convenient and often used shortcut for the <emph style="bold">ldeepindex</emph>-<emph style="bold">expr</emph>-<emph style="bold">ldeepset</emph> sequence. It returns the value of the element after increase.</para>
<quote><emph style="bold">ldeeprep</emph> <emph style="italic">lstvar path</emph> ?<emph style="italic">path</emph>?... <emph style="italic">first last element-list</emph></quote>
<para>Replace a range of elements in a sublist identified by the <emph style="italic">path</emph> with the elements from the <emph style="italic">element-list</emph>. It returns nothing.</para>
<para>This command is different from <emph style="bold">lreplace</emph> in two ways, hence the name change. First, it acts on data in a variable, not on a list as an argument. Second, the elements for replacement are contained in a list, not as separate elements on command line. Both differences were created for convenience of practical use, plus to allow the path to pick up the variable number of arguments. I have found that I always need to replace elements in a variable, not as a pass-through operator, and that I almost always need to insert elements from another list, not just some fixed set of elements.</para>
<quote><emph style="bold">ldeeppop</emph> <emph style="italic">lstvar path</emph> ?<emph style="italic">path</emph>?... <emph style="italic">count</emph></quote>
<para>Remove <emph style="italic">count</emph> elements from the end of the sublist identified by <emph style="italic">path</emph> in the variable and return them in a list.</para>
<para>This command was inspired by the pop operator in Perl. Somehow I&apos;ve never has a very string need for the other similar commands (which would be <emph style="italic">ldeeppush</emph> to add elements, and <emph style="italic">ldeepshift</emph> and <emph style="italic">ldeepunshift</emph> for operations on the start of the list) but they can be easily added as well for completeness.</para>
<para>The command returns the list of the popped elements in the original order. For example, if <emph style="italic">lstvar</emph> contained {0 1 2 3 4 5}, &quot;ldeeppop lstvar {} 2&quot; would return {4 5}, NOT {5 4}.</para>
</subsection>
</section>
<section title="Other Extensions for List Support">
<para>In my practice I have found that a few other commands make working with lists much more convenient. They are not directly related to the nested lists but to the lists in general.</para>
<quote><emph style="bold">lconcat</emph> <emph style="italic">sublist</emph> ?<emph style="italic">sublist</emph>?...</quote>
<para>Concatenate the argument lists and return the resulting list. This command is similar to <emph style="bold">concat</emph> but avoids converting the values to strings, concatenating the strings and then re-parsing the strings. When the lists involved grow to a few megabytes, <emph style="bold">concat</emph> can become very inefficient both in the sense of time and memory usage; <emph style="bold">lconcat</emph> resolved this inefficiency. Note that it does <emph style="italic">not</emph> replace <emph style="bold">concat</emph>, which can be used to assemble lists from pieces in different argument strings. The command returns the concatenated list.</para>
<para><emph style="italic">(Note that this is largely possible through using</emph> <emph style="bold">list</emph> <emph style="italic">and <tipref type="text" tip="157"/>/<tipref type="text" tip="293"/>, and that</emph> <emph style="bold">concat</emph> <emph style="italic">is more likely to handle the lists where this matters efficiently anyway right now due to efficiency tweaks to its implementation. It remains to be seen whether there is still a need for</emph> <emph style="bold">lconcat</emph> <emph style="italic">in other areas...)</emph></para>
</section>
<section title="Reference Implementation">
<para>The reference implementation is available as part of the Not A Commander project [<url ref="http://nac.sf.net/"/>], the source file <emph style="italic">cutil.c</emph>. To include the new commands into Tcl, the error messages will have to be adjusted to match the style used in Tcl, and the man pages will have to be written. The current implementation has been tested in fair amounts for both correctness and efficiency by usage in the Not A Commander project, the formal test suite would have to be written. Further progress in this direction depends on acceptance of this proposal.</para>
</section>
<section title="Comments">
<para>Messages on the TCLCORE mailing list have pointed out that nearly everything proposed here is either equivalent to, or trivially created by composition of the existing commands <emph style="bold">lindex</emph>, <emph style="bold">lrange</emph>, <emph style="bold">lset</emph>, <emph style="bold">lassign</emph>, and <emph style="bold">lrepeat</emph>. The only novel thing proposed is the ability of <emph style="bold">lset</emph> to create new list elements. If that&apos;s still desired, a separate new TIP proposing that alone would be the best way to deal with that.</para>
<para>I call on the author to withdraw this TIP.</para>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
<rule/>
</section>
<section title="Appendix: Removed Commands">
<para><emph style="italic">These commands were originally part of this TIP, but have been removed to this appendix on the grounds that the functionality they describe is now part of Tcl via other TIPs.</emph></para>
<quote><emph style="bold">ldeepindex</emph> <emph style="italic">list path</emph> ?<emph style="italic">path</emph>?...</quote>
<para>Extract an element from a nested list. The element is identified by the logical concatenation of the paths. It returns the extracted element. <emph style="italic">(Obsoleted by <tipref type="text" tip="22"/>.)</emph></para>
<quote><emph style="bold">ldeeplen</emph> <emph style="italic">list path</emph> ?<emph style="italic">path</emph>?...</quote>
<para>Find the length of a nested sublist. The sublist is identified by the logical concatenation of the paths. It returns the found length. A non-existing sublist is assumed to have the length of 0. <emph style="italic">(Obsoleted by <tipref type="text" tip="22"/> and normal llength.)</emph></para>
<quote><emph style="bold">ldeeprange</emph> <emph style="italic">list path</emph> ?<emph style="italic">path</emph>?... <emph style="italic">first last</emph></quote>
<para>Extract a sublist from a nested list. This command is a convenient equivalent of</para>
<verbatim><vline encoding='base64'>ICAgbHJhbmdlIFtsZGVlcGluZGV4IGxpc3QgcGF0aCA/cGF0aD9dIGZpcnN0IGxhc3Rd</vline></verbatim>
<para>&quot;end&quot; is supported as the first or last index. It returns the extracted sublist. <emph style="italic">(Obsoleted by <tipref type="text" tip="22"/> and normal lrange.)</emph></para>
<quote><emph style="bold">mset</emph> <emph style="italic">list-of-variable-names list-of-values</emph></quote>
<para>Set the values from the value list to the variable in the variable list at the same index. The command name stands for &quot;multiple set&quot;. This command is inspired by assignments in Perl.</para>
<para>If there were more variables than the values, the rest of variables are set to an empty value. If there were more values than variables, then the last variable is assigned the whole end of a list (as a list).</para>
<para>A special variable name &quot;<emph style="bold">-</emph>&quot; can be used to throw away a value, or the whole end of the value list if it&apos;s specified as the last one in the variable list.</para>
<para>The command returns nothing. It can be argued that it would make sense to return the length of the original list, or the difference between the length of the values list and the length of the variables list. I don&apos;t know which one is better.</para>
<para>This command is particularly convenient for returning multiple values from a procedure. For example:</para>
<verbatim><vline encoding='base64'>ICAgcHJvYyB4eHgge2EgYn0gew==</vline><vline encoding='base64'>ICAgICAgcmV0dXJuIFtsaXN0IFtleHByICRiKyRhXSBbZXhwciAkYiokYV1d</vline><vline encoding='base64'>ICAgfQ==</vline><vline encoding='base64'>ICAgbXNldCB7c3VtIHByb2R1Y3R9IFt4eHggMSAyXQ==</vline></verbatim>
<para><emph style="italic">(Obsoleted by <tipref type="text" tip="57"/>.)</emph></para>
<quote><emph style="bold">ldup</emph> <emph style="italic">count element</emph> ?<emph style="italic">element</emph>?...</quote>
<para>This returns a list produced by duplicating the sequence of elements <emph style="italic">count</emph> times. This command is inspired by the operator &quot;x&quot; in Perl, and it is a logical equivalent of:</para>
<verbatim><vline encoding='base64'>ICAgcHJvYyBsZHVwIHtjb3VudCBhcmdzfSB7</vline><vline encoding='base64'>ICAgICAgc2V0IHJlcyB7fQ==</vline><vline encoding='base64'>ICAgICAgZm9yIHtzZXQgaSAwfSB7JGkgPCAkY291bnR9IHtpbmNyIGl9IHs=</vline><vline encoding='base64'>ICAgICAgICAgc2V0IHJlcyBbbGNvbmNhdCAkcmVzICRhcmdzXQ==</vline><vline encoding='base64'>ICAgICAgfQ==</vline><vline encoding='base64'>ICAgICAgcmV0dXJuICRyZXM=</vline><vline encoding='base64'>ICAgfQ==</vline></verbatim>
<para><emph style="italic">(Obsoleted by <tipref type="text" tip="136"/>.)</emph></para>
<quote><emph style="bold">lvdup</emph> <emph style="italic">count element-list</emph></quote>
<para>This returns a list produced by duplicating the <emph style="italic">element-list count</emph> times. This command is inspired by the operator &quot;x&quot; in Perl, and it is a logical equivalent of:</para>
<verbatim><vline encoding='base64'>ICAgcHJvYyBsdmR1cCB7Y291bnQgbGlzdH0gew==</vline><vline encoding='base64'>ICAgICAgc2V0IHJlcyB7fQ==</vline><vline encoding='base64'>ICAgICAgZm9yIHtzZXQgaSAwfSB7JGkgPCAkY291bnR9IHtpbmNyIGl9IHs=</vline><vline encoding='base64'>ICAgICAgICAgc2V0IHJlcyBbbGNvbmNhdCAkcmVzICRsaXN0XQ==</vline><vline encoding='base64'>ICAgICAgfQ==</vline><vline encoding='base64'>ICAgICAgcmV0dXJuICRyZXM=</vline><vline encoding='base64'>ICAgfQ==</vline></verbatim>
<para><emph style="italic">(Obsoleted by <tipref type="text" tip="136"/> and <tipref type="text" tip="157"/>/<tipref type="text" tip="293"/>.)</emph></para>
</section>
</body></TIP>

