http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/lset.htm === lset varName ?index ...? newValue === The lset command accepts a parameter, ''varName'', which it interprets as the name of a variable containing a Tcl list. It also accepts zero or more indices into the list. The indices may be presented either consecutively on the command line, or grouped in a Tcl list and presented as a single argument. Finally, it accepts newValue, a new value for an element of varName. ---- Kevin Kenny writes on news:comp.lang.tcl.announce : Subject: TIP's 22, 33, 45 - extended [list] functionality - now final. Date: 18 Nov 2001 11:30:33 -0800 Message-ID: Jeff Hobbs today committed to the CVS HEAD at Sourceforge the changes described in TIP's 22 (Multiple Index Arguments to lindex), 33 (Add 'lset' Command to Assign to List [Element]s), and 45 (Empty index lists for [lindex] and [lset]) These changes augment the [lindex] command so that it can extract elements from sublists, for example: [lindex {{a b c} {d e f} {g h i}} 1 1] => e They also implement an [lset] command that may be used to change individual elements within lists and sublists. Taken together, these commands can be used to treat lists as if they were linear [array]s. For instance, the following [proc]edure might be used to reverse the order of elements in a list. proc reverse { list } { set i 0 set j [expr { [llength $list] - 1 }] while { $j > $i } { set temp [lindex $list $i] lset list $i [lindex $list $j] lset list $j $temp incr i incr j -1 } return $list } ---- [RS] on 2005-02-19 offers this variant that uses index arithmetics, so only one index has to be taken care of: proc reverse list { set i 0 while { 2*$i < [llength $list] } { set temp [lindex $list $i] lset list $i [lindex $list end-$i] lset list end-$i $temp incr i } return $list } [LES] on 2005-09-26 offers this variant, with a strange use of [string repeat], that still uses [lset] and is some 15~20 percent faster: proc reverse list { set _temp [ string repeat {x } [ llength $list ] ] set _nn [ expr [ llength $list ] - 1 ] set _c 0 for {set i $_nn } {$i >= 0} {incr i -1} { lset _temp $_c [ lindex $list $i ] incr _c } return $_temp } But it can be a lot faster if we lose [lset] altogether: proc reverse list { for {set i [ expr [ llength $list ] - 1 ] } {$i >= 0} {incr i -1} { lappend _temp [ lindex $list $i ] } return $_temp } ---- Updated documentation for the commands is available in the 'doc/' subdirectory in the source tree. The original proposals may be found at http://www.purl.org/tcl/tip/22.htm http://www.purl.org/tcl/tip/33.htm http://www.purl.org/tcl/tip/45.htm ---- [[Explain why lset is A Good Thing.]] [MAKR], 2007-12-14, stumbled over some timing weirdness when comparing [lrepeat] and [lset] ... anyway - [MS] explained in [Tcl'ers chat]: [[...]] makr: main (internal) diff is that ''lset'' works in-place whenever possible. So does [lreplace], but the fact that it operates on a value (and not a variable) makes it so that it is never possible when replacing in a variable's value [[...]] ie, `lset lst 0 a` will replace-in-place if `$lst` is not a shared obj; `lreplace $lst 0 0 a` ALWAYS gets a shared list as first argument: one ref as the value of the ''lst'' variable, another as lrepace's argument [[...]] so ''lreplace'' is forced to make a copy of the list and replace in the copy; when you are doing `set lst [[lreplace $lst 0 0 a]]` that is a pure waste ... except if ''lst'' happens to have read traces [[...]] so `lset lst 0 a` would be (more or less) the functional equivalent of `set lst [[lreplace [[K $lst [[set lst {}]]]] 0 0 a]]` or in the best current alternative `set lst [[lreplace $lst[[set lst {}]] 0 0 a]]`. ---- I have been trying the new command 'lset' and it is a nice addition to the core. However, it does not permmit to create new elements in the list. Why not? Today, there is not a simple method in TCL to convert the list: {a a} {b b} {c c} into: {a a} {b b b} {c c} it would be nice to do: lset list 1 2 b There are many mathematical algorithms that need to fill a list, but not necessarily from the beginning. So, a command that filled position n of the list and filled with "" the elements from 0 to n-1 would be a good adition to the lset command. For me, the best option is just change the lset especification to permmit to do so. Maybe other people would prefer an additional option: lset -create list 1 1 element I would like to collect here other opinions ---- [KBK] (22 October 2002) - The semantics you propose have been controversial. I think the next thing to do is the intermediate step of allowing [[lset]] to address the element past the end of a list, functioning as [[lappend]] if the designated element is not there. That at least allows for building up arrays in loops without needing to address sparse arrays. Sparse arrays are something of a can of worms. In the meantime, set sublist [lindex $list 1] lset list 1 {} lappend sublist b lset list 1 $sublist is about the best I can do for your example. [RS] wonders whether set sublist [lindex $list 1] lset list 1 [lappend sublist b] is equivalent - especially what the ''lset list 1 {}'' above is good for... ---- [ramsan] (23 october 2002) - I think that we should take advantage of the fact that [[lset]] is a new command for changing '''now''' its functionality to the '''best''' functionality. I see no benefit in going through an intermediate step. Which is the best functionality? For me, the best one is to permmit to create one element and all its predecessors when necessary. I would appreciate if someone can point out any advantage of the current implementation. Maybe we can give additional feedback to the TCT if we can join here some opinions on the subject. [SS] - the advantage of the current semantics is that it is more likely to find bugs causing the script to poke at random indexes in the array. it is not simple to decide if functionality is better than safety, btw I think a good idea is to create a command able to populate a range of the list with empty elements, leaving lset how it is today. Example: set foo {} lblank foo 0 100 for {set i 0} {$i < 100} {incr i} { lset foo $i $i; # => OK } lblank (very bad name, sorry, just an example) should probably accept even a single argument instead of a range, so that lblank foo 100 is an alias for lblank foo 100 100. An alternative is a simpler lresize , that just is able to resize the list to elements, adding null elements where needed (it may also cut the list if is < than ). [AMG]: In Tcl 8.6, lset is able to extend lists. [http://tip.tcl.tk/331] ====== set list {a a} {b b} {c c} lset list 1 2 b ;# returns {a a} {b b b} {c c} ====== ---- [Sarnold] I tried to rewrite a binary tree benchmark (found on http://shootout.alioth.debian.org/) some times ago and found that nested lists perform better this task than a flat list. Is it related to Tcl internals - I mean, when I use [lset], does the interpreter create a new Tcl_Obj structure each time ? In other languages I know, flat arrays are not much slower than structures pointing to others - and that is basically what nested lists are in Tcl, because of data sharing - so why ? ---- [Hai Vu] I found that lset has a side effect which squeezes multiple white space characters into one: ====== % set list " a b c " a b c % lset list 0 a a b c ====== [AMG]: [[lset]] produces canonical lists. [LV] Construct the value of your variable ''list'' as a list, and then see how things work. For instance, ====== % set list [list a b c ] a b c % set list [list { a} { b} { c }] { a} { b} { c } % lset list 0 a a { b} { c } ====== So, when provided a string which has not yet been ''[listify%|%listified]'', tcl turns the string into a list, then proceeds with the list operation. You will likely see this type of side effect when passing a string to a list operation. If this is not the behavior you desire, then making use of commands [list] or [split] may provide less unexpected behavior. [Hai Vu] We can make use of this side effect to squeeze multiple white space characters into one. Also, this serves as a reminder that in Tcl, a a string is not 100% the same as a list. <>Command