Version 10 of lshift

Updated 2009-03-03 21:55:08 by AK

lshift - shift list's elements, removing the first element and returning it as the value of the function

lshift listVar

This is not a command included in Tcl, but rather a common 'idiom' for in-place list manipulation used e.g. for implementing queues are for command line processing.

A proper implementation should throw an error when listVar does not exist, and also when listVar contains the empty list (or string).


Implementations

The following is taken from Tcl Gems though slightly modified:

proc lshift listVar {
    upvar 1 $listVar l
    set r [lindex $l 0]
    set l [lreplace $l [set l 0] 0]
    return $r
}

You can use this programming idiom also without having the lshift command, e.g. with the K combinator:

    K [lindex $l 0] [set l [lreplace $l [set l 0] 0]

Or more generalized:

lshift listVar ?count?

Shift list count elements and return the respective elements.

proc lshift {listVar {count 1}} {
    upvar 1 $listVar l
    set r [lrange $l 0 [incr count -1]]
    set l [lreplace $l [set l 0] $count]
    return $r
}

And finally a full fledged one with error handling:

proc lshift {listVar {count 1}} {
    upvar 1 $listVar l
    if {![info exists l]} {
        # make the error message show the real variable name
        error "can't read \"$listVar\": no such variable"
    }
    if {![llength $l]} {error Empty}
    set r [lrange $l 0 [incr count -1]]
    set l [lreplace $l [set l 0] $count]
    return $r
}

Different Interpretations

In lshift -Adding Unique Items to Lists of Fixed Length the command lshift puts an element on a list if it is not already there. It is used to implement a 'recently used' list

Nifty Tricks

In Tcl Gems lshift is used for command line arguments processing. In Stacks and Queues the genial RS names lshift qpop - if push appends to the end, then pop pops from the end (stack) and lshift (qpop) pops from the front (queue).

LEG would like to contribute the following method to iterate over lines of text, whether stored in a file or in a variable:

proc vGets {listVar lineVar} {
    upvar 1 $listVar l $lineVar line
    if {[catch {lshift l} line]} {return -1} else {string length $line}
}
if {..} {# text in variable "text"
    set lines [split $text \n]
    set getLine vGets
} else {# text in channel fd
    set lines $fd
    set getLine gets
}
while {[$getLine lines line]!=-1} {
   # do something with the line ..
}

LV note that within tcllib's struct module there is a "struct::list shift" command. Perhaps some of the additional functions recently written on the wiki might fit into the same module.