Tcl Part I · Module 6 · Lists
lrange: Taking a Slice
Where lindex gives you one element, this gives you a run of them as a new list. It follows the same index rules, so there is little new mechanism to learn, and it turns two common jobs into one-liners: discarding a header row before processing a file, and keeping only the most recent few entries. The one thing worth slowing down for is a distinction that looks like a technicality and is not — a slice of one element is a list containing that element, which is not the same value as the element itself.
Foundation10 min readTclPart IListsSlicing
Part I · Module 6 · Chapter 6.5 · lrange
1. The Command
set lines {header r1 r2 r3 r4}
puts "1 to 3 -> [lrange $lines 1 3]"
puts "0 to end -> [lrange $lines 0 end]"1 to 3 -> r1 r2 r3
0 to end -> header r1 r2 r3 r4A first and a last position, both inclusive, returning a new list.
Inclusive on both ends is the same rule as string range — Chapter 5.3 §3 — and the same difference from languages with exclusive upper bounds. 1 3 is three elements, not two.
2. The Two Jobs You Will Use It For
Dropping a header row:
set lines {"name width" "clk 1" "reset 1" "payload 64"}
set body [lrange $lines 1 end]
puts "rows: [llength $body]"
foreach row $body {
puts " $row"
}rows: 3
clk 1
reset 1
payload 64One line instead of a loop with a counter and a skip condition.
Keeping the last few:
set history {run1 run2 run3 run4 run5}
puts "last 3: [lrange $history end-2 end]"last 3: run3 run4 run5end-2 end is the idiom for "the last three", exactly as in Chapter 5.3 §3.
3. Out of Range Is an Empty List
set l {a b c}
puts "5 to 10 -> <[lrange $l 5 10]>"
puts "2 to 0 -> <[lrange $l 2 0]>"
puts "-5 to 1 -> <[lrange $l -5 1]>"5 to 10 -> <>
2 to 0 -> <>
-5 to 1 -> <a b>No errors. A range past the end is empty, a reversed range is empty, and a range starting before the list is clamped to the beginning.
Same behaviour as string range, and the same caution applies — an empty result from a miscalculated position looks exactly like an empty result from an empty input.
4. A Slice Is Not an Element
This is the distinction worth the chapter.
set l {{two words} beta}
set slice [lrange $l 0 0]
set element [lindex $l 0]
puts "slice: <$slice> llength [llength $slice]"
puts "element: <$element> llength [llength $element]"slice: <{two words}> llength 1
element: <two words> llength 2lrange $l 0 0 gave a list containing one element. lindex $l 0 gave the element.
With a single-word element the two print identically and you never notice. With a multi-word element they are clearly different values, and passing one where the other was expected changes the element count downstream.
5. Index Arithmetic Works
set l {a b c d e}
set i 2
puts "before i: [lrange $l 0 $i-1]"
puts "after i: [lrange $l $i+1 end]"before i: a b
after i: d eThe same no-expr arithmetic as Chapter 6.4 §2. This is the shape for splitting a list at a found position.
6. It Returns a New List
set l {a b c d}
lrange $l 1 2
puts "unchanged: $l"
set middle [lrange $l 1 2]
puts "stored: $middle"unchanged: a b c d
stored: b cNothing is modified. To remove a span from a variable you want lreplace, which is the next chapter.
7. Taking a Page at a Time
A pattern worth seeing, since it combines what you have:
set items {a b c d e f g}
set page_size 3
set page 0
while {$page * $page_size < [llength $items]} {
set first [expr {$page * $page_size}]
set last [expr {$first + $page_size - 1}]
puts "page $page: [lrange $items $first $last]"
incr page
}page 0: a b c
page 1: d e f
page 2: gThe last page is short and needs no special handling, because a range running past the end is clamped rather than failing — §3 working in your favour.
while is Module 9; the point here is that lrange does the awkward part.
8. Common Mistakes
Expecting an exclusive end. Both ends are inclusive. §1.
Confusing a one-element slice with the element. §4.
Using positions from a search without checking. Chapter 6.3 §3.
Expecting it to modify the list. It returns a new one. §6.
Reaching for a loop to drop a header. §2.
9. Exercises
Exercise 1 — slices. From {a b c d e}, produce b c d, d e, the whole list, and an empty list, each with one lrange.
Exercise 2 — inclusive ends. How many elements does lrange $l 1 3 return? Predict, then confirm.
Exercise 3 — drop and keep. Given a six-line list, drop the first line and keep the last two, in two separate statements.
Exercise 4 — slice versus element. Build a list whose first element is two words. Print lrange $l 0 0 and lindex $l 0 with their lengths, and explain the difference.
Exercise 5 — out of range. Predict the result of lrange {a b c} 10 20 and lrange {a b c} 2 1. Then say why neither errors.
Exercise 6 — split at a position. With a five-element list and set i 2, produce the elements before i and after i without using expr.
10. Remember
lrange list first lastreturns a new list, with both ends inclusive.endandend-Nwork, as does arithmetic like$i-1.lrange $l 1 enddrops a header;lrange $l end-2 endkeeps the last three.- Out of range gives an empty list, a reversed range gives an empty list, and a range starting before the list is clamped.
- A one-element slice is a list, not the element.
lindexfor a value,lrangefor a list. - It returns a new list and never modifies the original.
Next: adding to a list.
Where this fits
Part of the Tcl curriculum.
