Tcl Part I · Module 6 · Lists
llength: How Many Elements
One command with one argument, and almost nothing to learn about its mechanism. What makes it worth a chapter is where you put it: a list that came from a search, a file, or a split is a list that might have nothing in it, and checking that before you start indexing is the difference between a clear error and a confusing empty result several lines later. This chapter also collects the handful of edge cases people guess wrong — most importantly that it never complains about text that does not look like a list.
Foundation9 min readTclPart IListsCountingDefensive Checks
Part I · Module 6 · Chapter 6.3 · llength
1. The Command
puts [llength {alpha beta gamma}]
puts [llength {}]3
0One argument, one number. That is the whole command.
2. The Edge Cases
Four results worth knowing, because at least one of them will surprise you:
puts "empty braces: [llength {}]"
puts "empty string: [llength ""]"
puts "a bare word: [llength "hello"]"
puts "nested: [llength {a {b c} d}]"
puts "extra spaces: [llength "a b c"]"empty braces: 0
empty string: 0
a bare word: 1
nested: 3
extra spaces: 3- The empty string is an empty list. Chapter 6.1 §7.
- A bare word is a one-element list.
llength "hello"is 1, not an error and not 5. Any text that parses as a list has a length. - A braced group counts as one element, however many words are inside it.
- Runs of whitespace count once — which is the contrast with
splitfrom Chapter 5.13 §5, where the same text gave four elements.
That last pair is worth holding together:
set s "a b c"
puts "llength: [llength $s]"
puts "llength of split: [llength [split $s]]"llength: 3
llength of split: 4Reading text directly as a list is usually what you want for whitespace-separated data. split is for a specific separator character.
3. The Check It Exists For
A list that came from somewhere else might be empty:
set results {}
if {[llength $results] == 0} {
puts "nothing to do"
} else {
puts "processing [llength $results] items"
}nothing to doThat is the shape, and it is better than the three alternatives people reach for.
Not if {$results eq ""} — that happens to work for an empty list, but it is asking a text question about a list, and it fails for a list containing one empty element.
Not if {[info exists results]} — existence is Chapter 3.4's question and is a different one. A variable can exist and hold an empty list.
Not relying on a search result — Chapter 6.9 returns -1, not an empty list.
set a {}
set b [list ""]
puts "a: llength [llength $a], eq-empty [expr {$a eq ""}]"
puts "b: llength [llength $b], eq-empty [expr {$b eq ""}]"a: llength 0, eq-empty 1
b: llength 1, eq-empty 0The second list has one element, and that element is empty. A text comparison would not have told you that; llength does.
4. It Errors on Text That Is Not a List
puts [catch {llength "a {b c"} message]
puts $message1
unmatched open brace in listThis is Chapter 6.1 §5. Unbalanced braces mean the text cannot be read as a list at all.
It is rare with data you built yourself and possible with data from outside. If you are counting something whose shape you do not control, wrap it:
set raw "a {b c"
if {[catch {llength $raw} n]} {
puts "not a valid list: $raw"
} else {
puts "$n elements"
}not a valid list: a {b ccatch is Module 14; this is the one place in the module where it earns a mention.
5. Counting Rows and Columns
With nested lists — Chapter 6.11 — llength counts one level:
set table {{clk 1} {reset 1} {payload 64}}
puts "rows: [llength $table]"
puts "columns in row 0:[llength [lindex $table 0]]"rows: 3
columns in row 0:2There is no command that counts every element at every depth, because a list does not know how deep it is. You count the level you are looking at.
6. A Worked Example
set lines {
"clk 1 input"
""
"payload 64 output"
}
set used 0
set skipped 0
foreach line $lines {
set fields [string trim $line]
if {[llength $fields] != 3} {
incr skipped
continue
}
incr used
}
puts "$used usable, $skipped skipped"2 usable, 1 skippedChecking the element count is how you validate a record's shape — the list equivalent of Chapter 5.12 §4's "check the count that scan returned".
7. Common Mistakes
Using eq "" to test for an empty list. §3. It misses a list holding one empty element.
Confusing existence with emptiness. §3.
Expecting an error for non-list-looking text. llength "hello" is 1. §2.
Expecting llength and split to agree. §2.
Indexing without checking. §3.
Expecting it to count nested elements. It counts one level. §5.
8. Exercises
Exercise 1 — predict six. Without running: llength {a b c}, llength {}, llength hello, llength {a {b c}}, llength "x y", llength [list ""].
Exercise 2 — empty versus one-empty. Build both an empty list and a list containing one empty element. Show with llength that they differ, and show that a text comparison does not distinguish them reliably.
Exercise 3 — the guard. Write a script that prints the first element of a list, or "nothing found" when the list is empty. Test it both ways.
Exercise 4 — split disagreement. Take "a b c" and print its length as a list and as the result of split. Which would you use for whitespace-separated fields, and why?
Exercise 5 — validate a record. Given a list of lines, count how many have exactly three fields and how many do not, as in §6.
Exercise 6 — rows and columns. For {{a 1 x} {b 2 y}}, print the number of rows and the number of columns in the second row.
9. Remember
llengthreturns the number of elements in one list, with no options.- The empty string and
{}both give 0. - A bare word gives 1. It does not error on text that does not look like a list.
- A braced group is one element, and runs of whitespace count once.
- That differs from
split, which does not collapse whitespace. llength $x == 0is the honest test for "nothing" — better than a text comparison or an existence check.- A list holding one empty element has length 1, which a text comparison would miss.
- It errors on unbalanced braces. Wrap in
catchfor data you do not control. - It counts one level only.
Next: reading an element out.
Where this fits
Part of the Tcl curriculum.
