Version 37 of lappend

Updated 2008-06-04 08:48:49 by jcw

lappend varName ?value value ...?


http://www.purl.org/tcl/home/man/tcl8.4/TclCmd/lappend.htm


Appends each value as a list element onto the end of the list stored in varName.

lappend used to be the only list-command which requires the name of a variable containing a list rather than the list itself, but in Tcl 8.4 the lset command was introduced.

 lappend listName

rather than

 lappend $listName

This command differs from append in that values are appended as list elements rather than raw text.


lappend will not make varName a list ; if it is one before the command, it will continue to be one.

AJD Uh? lappend *will* attempt to make varName's value into a list. The docs say "This command treats the variable given by varName as a list." ie. if it isn't a list to begin with, tcl will attempt to convert it to one, giving an error this is not possible:

 % set x "a \{" ;# string
 a {
 % lappend x b ;# treat it as list
 unmatched open brace in list ;# it wasn't a proper list!

LV Actually, the example here of x and b demonstrates that Tcl does not try to make varName into a list, but instead presumes that it is a valid list. If it attempted to turn the variable into a list, it would surely succeed; in this case, however the basic assumption of (nearly?) all the l* commands is that list arguments are proper lists and that no attempt will be made to second guess the caller.

Lars H: It seems you disagree about what it means to "attempt" something. What lappend does is that it attempts to generate a list internal representation for the current value of the variable (i.e., it calls tclListType.setFromAnyProc [L1 ]), and then appends the element(s) to that. This attempt will fail when the string representation cannot be interpreted as a list.

LV Actually, what I was disagreeing with was the idea the phrase "make varName's value into a list". This was based on my interpretation of the docs - which was a mistake on my part. However, user of lappend needs to understand that lappend will not turn a string value into a list; if the string is not a list, then a call to lappend fails. And that's really the bottom line of the discussion. Sorry for the digression.


You do not need to assign the results of lappend to varName; lappend modifies varName directly.


How would one append an item to the beginning of a list? Since append means to the end of, one wouldn't append to the beginning of a list. Instead, for a way to perform the equivalent of prepend, see linsert -- jfr

LV [tried proposing a couple ways to prepend, all of which failed... RS [provided the comment that...]]]

   set a [concat $toPrepend $a]

results in the 4-element list (maybe via shimmering over string reps)

   xyz 1 2 3 

linsert would keep an existing list representation, so I presume its faster to do

 set a [linsert $a 0 $toPrepend]

slebetman - If I'm not mistaken, concat have been optimised in 8.4 to not shimmer when processing pure lists. In fact it is even faster than linsert:

  % set a [list a b c]
  % time {set a [linsert $a 0 d]} 10000
  41.1639 microseconds per iteration

  % set a [list a b c]
  % time {set a [concat d $a]} 10000
  4.8214 microseconds per iteration

Lars H: As far as I can tell, this is not using the pure list optimisations of concat -- you're seeing the string performance of that command! If you try it with larger list elements, performance should start to favour linsert instead. You might also want to check what happens if you rewrite the above using the "K combinator" to let linsert operate on an unshared Tcl_Obj; this appears to be the case that linsert is optimised for (even though it is probably rather rare, hmm...).

slebetman - Quite right, testing with large 100 character strings gives me 36.7218 microseconds per iteration for linsert but a staggering 1609.4685 microseconds per iteration for concat - yikes!


RS 2006-10-01: jcw contributed this (for me) surprising idiom:

 lappend var

does nothing, but creates var if it doesn't exist. Similar to, but simpler than:

 if {![info exists var]} {set var ""}

This may however fail if $var holds a string that cannot be parsed as a list.

wdb It works, but it is some kind of dirty trick as it is based on some Tcl-internal optimizing located far out of documented behaviour. The greater danger is not to throw an error. Exemplum gratia, here lappend works as you describe:

 % set a "a {b} c"
 a {b} c
 % lappend a
 a {b} c

but here not:

 % set a "a {b} c"
 a {b} c
 % lappend a x
 a b c x

Unintended side effect: element b has "forgotten" its surrounding braces as they are not necessary for identifying b as a list element.

I don't think that this will fit to the category quick'n'dirty as it is perhaps dirty enough but not quick if you are hunting the resulting bugs on friday afternoon ;-)

Lars H: Well, if you care about the braces, then apparently you don't merely see the value as a list, and in that case using lappend probably isn't the right thing to do (as it will only respect the list interpretation of the value). jcw has also been seen using append in the same way, when the value is a more general string.


jmn 2008-06-04 I find myself using code like the following very often:

 if {$item ni $list} {
  lappend list $item
 }

I know it's not much code.. but It seems to me it'd be neat to have an option so you could instead do something like:

 lappend -ifmissing list $item

This indicates you're using a list as a set. You could also consider using 8.5's "dict set list $item {}" for a slightly different approach -jcw


See also list, lindex, linsert, llength, lrange, lreplace, lsearch, lsort .


Tcl syntax help - Arts and crafts of Tcl-tk programming - Category Command