concat , a built-in command, joins strings together such that if the strings are lists, they are concatenated into one into one list.
concat trims the leading and trailing whitespace from each argument, discards all arguments that after trimming are the empty string, and then joins the remaining arguments together with a single space between each pair of arguments. The result of concat with no arguments is the empty string. The procedure is named concat because its main (only?) use is to concatente lists: If all the arguments are lists, the result is a list composed of all the items in the given lists. Due to the strategy of defining errors out of existence, concat operates on any strings, not just lists. When the arguments are not lists, the result may not be a list, or may happen to be a list, but not the list that was expected.
Lars H PYK: eval forms the script to evaluate by concatenaing its arguments just as concat does, and if the result is a list that has no string representation it can skip the substitution step since such a list is known to contain no substitutions, which improves performance.
{*} can also be use to the same end as concat:
# Instead of: set foo [concat $bar $boo $spong] set foo [list {*}$bar {*}$boo {*}$spong]
concat a b {c d e} {f {g h}}
which produces the value
a b c d e f {g h}
[concat] has no problem with strings that are not well-formed lists:
concat " a b {c " d " e} f" ;# -> a b {c d e} f
The result happens to be a valid list, but the inputs were not:
% string is list -strict " a b {c " 0 % string is list -strict d 1 % string is list -strict " e} f" 0 % string is list -strict [concat " a b {c " d " e} f"] 1
[concat] also happily returns values which are not well-formed lists:
set l [concat \{ a b c] lindex $l 0 ;# -> unmatched open brace in list
AMG PYK: [concat] is defined in terms of string concatenation; list concatenation is "merely" an optimization applied when all arguments are pure lists. See tclUtil.c . In the previous example, the first argument is not a valid list, let alone a pure list. [string is list \{] returns 0. concat's remaining arguments aren't pure lists either, even though they're valid lists.
[concat] does not modify its inputs in any way, except to insert a space between them. It does not, for example, remove spaces from the middle of its arguments:
concat "a b c" { d e f } #; -> a b c d e f
In the result, there are still three spaces between a, b and c.
To make sure the inputs are valid lists, use [lappend] instead:
lappend mylist {*}$myotherlist
See Concatenating lists for a timing comparison of various methods.
Other methods of putting strings together include:
set a abc set b 127 set c $a$b
set c [format {%s %s} $a $b]
join [list $string1 $string2]
set list [concat {} a b] llength $list ;# -> 2 set list [concat {{}} a b] llength $list ;# -> 3
slebetman - If I'm not mistaken, concat has 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!
Lars H: Good advices when experimenting with these things are:
Concatenating the sublists of a list (e.g. a matrix) is best done with join. However, concat can also be used as follows:
In Tcl 8.5, the proper way will be to use {*}:
concat {*}$matrix
In Tcl 8.4 we made do with
eval [list concat] [lrange $matrix 0 end]
or
eval [linsert $matrix[set matrix {}] 0 concat]
In most reasonable cases,
eval concat $matrix
works as well, but it give unpleasant surprises if there is a newline character between two elements of the $matrix.
The conversation below is about an issue that was fixed in Tcl version 8.5.
The issue wasn't specifically about concat, and the example discussed is a rather verbose way of illustrating that in older versions of Tcl the string representation of a floating point number wasn't always precise enough to unambiguously reflect the number. For example, the string representation of expr {1.0 / 3.0} was previously
0.333333333333
, whereas in later versions it is
0.3333333333333333
Procedures like concat, join, etc. could lead to this situation because they may result in the internal numeric representations of individual values being discarded, leaving only the lower-precision string representation. In modern versions of Tcl the string representations are sufficiently precise, so this is no longer an issue. Also, built-in Tcl procedures generally take care to avoid discarding internal representations when possible, although there continues to be room for improvement.
DBaylor: I find the behavior of concat bizarre. Numerical precision is lost with concat also - sometimes. At least with join you know you're losing precision. Here's an example:
set a [expr {1.0 / 3.0}] set list_aa [concat [list $a] [list $a]] # prints 1.0 puts [expr {3.0 * [lindex $list_aa 0]}] set list_a0 [concat [list $a] [list]] # prints 0.9999... puts [expr {3.0 * [lindex $list_a0 0]}]
Lars H: It seems you have found a counterexample to the rule that "The result of list is a pure list." -- namely that the empty list returned by list is just the empty string. I'd say this is a bug. Do you wish to report it, or should I? (That it matters at all is of course also a bug, but that one is deep and harder to fix. KBK has a TIP for 8.5 which will address it.)
Lars H: TIP #132 . It fixes the issue that conversion to string may cause loss of precision.
AMG: Oh, I misunderstood. I thought that TIP would be to make [list] return a pure list, not an empty string. But I do appreciate what #132 does.
LV 2006-12-05: so, did the "[list ] " is not returning a pure list report ever get filed at tcl.sf.net?
Lars H: Yes, it is #1143805 [L1 ]. The reply was mostly that "we prefer it the way it is; closing report".
[Lars H]: Offered comments on floating point precision and `[eval]`.