'''[http://www.tcl.tk/man/tcl/TclCmd/lset.htm%|%lset]''', a [Tcl Commands%|%built-in] Tcl command, places a value into a position in a [list]. ** Synopsis ** : '''lset''' ''varName'' ?''index ...''? ''newValue'' ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/lset.htm%|%official reference]: [http://core.tcl.tk/tcl/finfo?name=doc/lset.n%|%source code for official documentation]: [http://www.tcl.tk/cgi-bin/tct/tip/22.htm%|%TIP #22]: Multiple Index Arguments to lindex [http://www.tcl.tk/cgi-bin/tct/tip/33.htm%|%TIP #33]: Add 'lset' Command to Assign to List Elements [http://www.tcl.tk/cgi-bin/tct/tip/45.htm%|%TIP #45]: Empty index lists for [lindex] and [lset] [http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/4d61fee2651c264e/8255c26486944e1d?lnk=gst&q=lesser+light#8255c26486944e1d%|%re: tcl for beginner?] ,[comp.lang.tcl] ,2002-03-11: a reply from [kbk] explaining some of the rationale, poltics, design, and usage of [[`lset`] and [[`[lindex]`] [http://www.tcl.tk/cgi-bin/tct/tip/331.htm%|%TIP #331]: Allow [lset] to Extend Lists ** Description ** ''varName'' is the name of a variable whose value is a list. The first ''index'' argument, if it exists, specifies the cardinal index of an item in the list. Each additional ''index'' argument specifies the cardinal index of an item in the list specified by the previous index. It is an error if the item specified by the previous ''index'' argument is not a valid list. ''newValue'' is an argument whose value will be assigned to the position specified by the indexes. If there is only one ''index'' argument, it will be interpreted as a list of indexes, exactly as if each index was a separate argument to the command. Example: 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] 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] 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 } ====== ** Appending to Nested Lists ** `lset` is the command to use to append to nested lists: ====== set mylist {{one {1 2 3}} {two {2 3 4}} {three {3 4 5}}} lset mylist 1 1 end+1 5 puts $mylist ====== result: ====== {one {1 2 3}} {two {2 3 4 5}} {three {3 4 5}} ====== ** History ** [Kevin Kenny] announced [[`lset`] on [comp.lang.tcl.announce] , 2001-11-18. ** Discussion ** ---- [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 [[...]] i.e., `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 [[`[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: ======none {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] 2002-10-22: 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] 2002-10-23: 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. See [http://tip.tcl.tk/331%|%TIP #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 `$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 [[`[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. [AMG]: Be careful! Not all strings are valid lists. <> Command | list