Tcl Part I · Module 6 · Lists
lindex, and Counting From the End
Reading one element out of a list uses the same index vocabulary you already learned for strings, so most of this chapter will feel familiar: positions start at zero, end counts from the right, and simple arithmetic works inside the position. Two things are new. Out-of-range behaviour is worth stating plainly because it is forgiving in a way that hides mistakes, and there is a multi-index form that reaches straight into a nested list without pulling the intermediate levels out first.
Foundation11 min readTclPart IListsIndexing
Part I · Module 6 · Chapter 6.4 · lindex
1. Positions Start at Zero
set fields {alpha beta gamma delta}
puts "0 -> [lindex $fields 0]"
puts "2 -> [lindex $fields 2]"0 -> alpha
2 -> gammaSame as Chapter 5.3, except the unit is an element rather than a character.
2. The Same Index Vocabulary
Everything you learned for string indexing applies:
set fields {alpha beta gamma delta}
puts "end -> [lindex $fields end]"
puts "end-1 -> [lindex $fields end-1]"
set i 2
puts "i-1 -> [lindex $fields $i-1]"
puts "i+1 -> [lindex $fields $i+1]"end -> delta
end-1 -> gamma
i-1 -> beta
i+1 -> deltaend and end-N are index keywords, and simple arithmetic works inside the position with no expr.
Being able to write end means you never compute [expr {[llength $l] - 1}], which is where off-by-one errors come from.
3. Out of Range Is Empty
set fields {alpha beta}
puts "99 -> <[lindex $fields 99]>"
puts "-1 -> <[lindex $fields -1]>"99 -> <>
-1 -> <>No error. An index past either end gives an empty string.
4. No Index at All
set fields {alpha beta gamma}
puts [lindex $fields]alpha beta gammaCalled with no index, lindex returns the whole list unchanged.
That sounds useless and occasionally is not — it means a loop that peels one index at a time terminates sensibly rather than erroring. Mostly it is worth knowing so that an index coming from a variable that happens to be empty does not surprise you.
5. Reaching Into Nested Lists
Several indices drill down one level each:
set table {{clk 1} {reset 1} {payload 64}}
puts "whole row 2 -> [lindex $table 2]"
puts "row 2, col 0 -> [lindex $table 2 0]"
puts "row 2, col 1 -> [lindex $table 2 1]"whole row 2 -> payload 64
row 2, col 0 -> payload
row 2, col 1 -> 64lindex $table 2 0 means "element 2 of the list, then element 0 of that". It is the same as nesting two calls:
set table {{clk 1} {reset 1} {payload 64}}
puts [lindex [lindex $table 2] 0]
puts [lindex $table 2 0]payload
payloadThe multi-index form reads better and is the idiomatic one. Chapter 6.11 uses it throughout.
6. Common Shapes
First and last:
set queue {alpha beta gamma}
puts "first: [lindex $queue 0]"
puts "last: [lindex $queue end]"first: alpha
last: gammaPulling named fields out of a record:
set row [split "clk,1,input" ","]
puts "name: [lindex $row 0]"
puts "width: [lindex $row 1]"
puts "direction: [lindex $row 2]"name: clk
width: 1
direction: inputThat works and there is a better way to write it. Three lindex calls with bare numbers tell a reader nothing about what the columns mean, and renumbering them when a column is added is error-prone. Chapter 6.14 replaces all three lines with one.
7. It Does Not Modify the List
set l {a b c}
lindex $l 0
puts "unchanged: $l"unchanged: a b clindex reads. To change an element you want Chapter 6.8, which is the one list command that writes to the variable directly.
8. Common Mistakes
Indexing without checking the length. §3. The main one.
Computing the last index with llength. Use end. §2.
Expecting an error for an out-of-range index. You get "". §3.
Using lindex on a string expecting characters. lindex "hello" 0 is hello, because that text is a one-element list. For characters use string index — Chapter 5.3.
Nesting lindex calls by hand. The multi-index form is clearer. §5.
A chain of numbered lindex calls for a record. Use lassign. §6.
9. Exercises
Exercise 1 — positions. For {red green blue yellow}, print elements 0, 2, end and end-2.
Exercise 2 — out of range. Print element 10 of a three-element list between angle brackets. Then write the check that would have caught it.
Exercise 3 — the string trap. Predict lindex "hello" 0 and string index "hello" 0. Run both and explain why they differ.
Exercise 4 — nested. For {{a 1} {b 2} {c 3}}, pull out the value 2 using the multi-index form, then again using two nested calls.
Exercise 5 — index arithmetic. With set i 1, print the element before and after position i in a four-element list, without using expr.
Exercise 6 — the record smell. Take the three-lindex record example from §6 and say what breaks if a column is inserted at the front. Then look ahead to Chapter 6.14 and say how it helps.
10. Remember
lindex list indexreads one element. Positions start at 0.- Same vocabulary as string indexing:
end,end-N, and arithmetic like$i-1inside the position. - Out of range returns an empty string, never an error — at either end.
- So check
llengthfirst when the list came from outside. - With no index it returns the whole list.
- Several indices drill into nested lists:
lindex $t 2 0. - It never modifies the list.
lsetis for writing. lindex "hello" 0ishello, because that is a one-element list.
Next: taking several elements at once.
Where this fits
Part of the Tcl curriculum.
