Skip to content
VLSI Mentor

Tcl Part I · Module 6 · Lists

lset: Changing One Element in Place

This is the counterpart to lindex: where that one reads an element, this one writes it. It is the second of the two commands in this module that change your variable rather than handing back a new list, and it has one capability that makes it genuinely worth reaching for — it can update a single cell inside a nested structure without you having to pull the row out, change it, and put it back. It also behaves differently from lindex in one respect that will catch you if you assume otherwise.

Foundation10 min readTclPart IListsModificationNested

Part I · Module 6 · Chapter 6.8 · lset

1. The Command

Azvya Education Pvt. Ltd.VLSI Mentor
basic.tcl
set row {alpha beta gamma}

lset row 2 "updated"

puts $row
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
alpha beta updated

It takes a variable name — no $ — a position, and a new value. The variable is changed directly.

That makes it one of only two list commands that write, the other being lappend. Everything else returns a new list — Chapter 6.7 §2 has the table.

2. Compared With lreplace

lreplace can do the same job and is clumsier for it:

Azvya Education Pvt. Ltd.VLSI Mentor
versus.tcl
set a {alpha beta gamma}
set b {alpha beta gamma}

lset a 2 "updated"
set b [lreplace $b 2 2 "updated"]

puts "lset:     $a"
puts "lreplace: $b"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lset:     alpha beta updated
lreplace: alpha beta updated

Same result. lset says "change element 2" where lreplace says "replace the span from 2 to 2", which is a roundabout way to express the same intent — and it needs the assignment back.

Use lset for one element; lreplace for a span or a deletion.

3. Reaching Into Nested Lists

This is where it earns its place. Several indices address a cell inside a nested list:

Azvya Education Pvt. Ltd.VLSI Mentor
nested.tcl
set table {{clk 1} {reset 1} {payload 64}}

lset table 1 0 "rst_n"

puts $table
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
{clk 1} {rst_n 1} {payload 64}

One line changed the first element of the second row.

Without lset you would pull the row out, change it, and put it back:

Azvya Education Pvt. Ltd.VLSI Mentor
the-long-way.tcl
set table {{clk 1} {reset 1} {payload 64}}

set row [lindex $table 1]
set row [lreplace $row 0 0 "rst_n"]
set table [lreplace $table 1 1 $row]

puts $table
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
{clk 1} {rst_n 1} {payload 64}

Three lines, a temporary variable, and two chances to get an index wrong. The indices read the same way as Chapter 6.4 §5's multi-index lindex — row first, then column.

4. Out of Range Is an Error

Here is where it differs from lindex:

Azvya Education Pvt. Ltd.VLSI Mentor
out-of-range.tcl
set row {a b c}

puts [catch {lset row 9 "x"} message]
puts $message
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1
list index out of range

An error — not a silent no-op, and not an automatic extension of the list.

Compare with reading:

Azvya Education Pvt. Ltd.VLSI Mentor
read-versus-write.tcl
set row {a b c}

puts "lindex 9 -> <[lindex $row 9]>"
puts "lset 9   -> [catch {lset row 9 x}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lindex 9 -> <>
lset 9   -> 1

Reading past the end is silent; writing past the end is an error.

That asymmetry is the right design. An out-of-range read might legitimately mean "there is nothing there". An out-of-range write cannot mean anything sensible — there is no element to change — so Tcl refuses rather than guessing whether you wanted the list extended.

5. end Works

Azvya Education Pvt. Ltd.VLSI Mentor
end.tcl
set row {a b c}

lset row end "last"
puts $row

lset row end-1 "middle"
puts $row
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a b last
a middle last

The same index vocabulary as everywhere else in the module.

6. It Returns the New List Too

Azvya Education Pvt. Ltd.VLSI Mentor
returns.tcl
set row {a b c}

puts "returns: [lset row 1 "X"]"
puts "row is:  $row"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
returns: a X c
row is:  a X c

Both modifies and returns, like lappend and incr. The return value is rarely used.

7. Updating a Table

The shape you will write:

Azvya Education Pvt. Ltd.VLSI Mentor
table-update.tcl
set table {{clk 1 input} {reset 1 input} {payload 64 output}}

# widen the payload
lset table 2 1 128

# flip reset to active-low naming
lset table 1 0 "rst_n"

foreach row $table {
    puts [format "  %-10s %4s %s" {*}$row]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  clk           1 input
  rst_n         1 input
  payload     128 output

Two updates, two lines, no temporaries. The {*}$row in the format call is Chapter 6.13, spreading the row's three elements into three arguments.

8. Common Mistakes

Writing lset $row 1 x. The name goes in without a $.

Expecting it to extend the list. Out of range is an error. Use lappend. §4.

Expecting silence like lindex. Writing is stricter than reading. §4.

Using lreplace for a single element. lset is clearer. §2.

Pulling a row out to change one cell. Use several indices. §3.

Using a deeply indexed lset on data that should be a dictionary. §7.

9. Exercises

Exercise 1 — change one. Set a three-element list, change the middle element with lset, and print before and after.

Exercise 2 — lset versus lreplace. Do the same change both ways. Which needed an assignment, and why?

Exercise 3 — a nested cell. For {{a 1} {b 2} {c 3}}, change the 2 to 20 with one lset. Then read it back with lindex using the same indices.

Exercise 4 — the asymmetry. On a three-element list, read index 9 and then try to write index 9. Explain why one is silent and the other is not.

Exercise 5 — end. Change the last and second-to-last elements of a four-element list without using llength.

Exercise 6 — extend or change. You have a three-element list and want a fourth element. Say why lset cannot do it and write the line that can.

10. Remember

  • lset varname index value changes one element in place. No $ on the name.
  • It is one of only two list commands that modify the variable — the other is lappend.
  • Several indices reach into nested lists: lset table 1 0 value.
  • Those are the same indices lindex would use to read the cell.
  • Out of range is an error, unlike lindex, which returns an empty string.
  • It cannot extend a list. Use lappend to add.
  • end and end-N work.
  • Prefer it to lreplace for a single element; prefer lreplace for spans and deletions.

Next: finding an element rather than addressing one.

Where this fits

Part of the Tcl curriculum.