Updated 2018-03-26 10:01:12 by pooryorick

lappend appends elements to a list.

Synopsis  edit

lappend varName ?value value ...?

Description  edit

lappend creates a variable named varname if it does not exist, appends each value to the list stored in varName, and returns the value of varName. lappend differs from append in that values are appended as elements in the list rather than raw text.

lappend operates on the specified list in-place, so it is not necessary to assign the results of lappend to varName. This is why the first argument to lappend is the name of a variable rather than the list value itself.

It is an error if the value of the variable named varName is not a list.
% set x \{ ;# string that is not a list
% lappend x b ;# treat it as list
unmatched open brace in list

lappend auto_path /usr/local/lib/tcl8.5

#Does nothing but fail if listName is not a well-formed list.
lappend listName

Incorrect usage:
#warning: bad code ahead
set auto_path [lappend $auto_path /usr/local/lib/tcl8.5]

lappend $listName

the problem in the set example is not the use of lappend in the [ ... ] construct, but the use of $auto_path as the first argument instead of auto_path (without the $).

List Validation  edit

Another function of lappend is to validate a value as a well-formed list. When invoked with no values, lappend simply makes sure that the value of the variable named by varName is a valid list, and returns it:

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: word b has "forgotten" its surrounding braces as they are not necessary for identifying b as a word .

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.

Unique [lappend]  edit

jmn 2008-06-04:

I find myself using code like the following very often:
if {$element ni $list} {
 lappend list $element

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 $element

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

AMG: Faster, too, though it honks up the string representation to have every other word be empty string. Use [dict keys] to get your data back without the empties.

If you don't have [dict], use [set list($word) {}] instead, then [array names] will give you the list of word. Though you're using ni, so I'm sure you will have [dict]...

RS 2008-06-04: I just add a convenience proc whenever I need that functionality:
proc ladd {_list el} {
   upvar 1 $_list list
   if {$el ni $list} {lappend list $el}

List concatenation  edit

AMG: [lappend] can be used with the {*} expansion operator to concatenate lists.
lappend listVariable {*}$otherListVariable
lappend listVariable {*}[scriptReturningList]

But what if you don't have {*}? Then you can use [eval] or [concat]. Here's a demonstration of the latter:
set listVariable [concat $listVariable $otherListVariable]
set listVariable [concat $listVariable [scriptReturningList]]

There is one subtlety being missed. [lappend] is a creative writer, meaning that it creates the named variable if it doesn't already exist. The above code will not do this since it starts by taking its value to get the first argument to [concat]. Solution? Use [append] to append empty string to the variable, which creates it if it's not there but otherwise leaves it unchanged. What's more, [append] returns the variable's value, so it all fits in a one-liner, functional context:
set listVariable [concat [append listVariable {}] $otherListVariable]
set listVariable [concat [append listVariable {}] [scriptReturningList]]

See also  edit