Here's a couple handy little gems I like to have available. The one-letter ones are not always the most efficient way of doing the task, but when the line's getting a little convoluted, these things can make it a little more readable. At least, I think so...
interp alias {} nocase {} string equal -nocase
interp alias {} L {} list
proc K {args} {lindex $args 0}
proc U {varName} {
uplevel 1 [subst -noc {lindex [list [set $varName] [unset $varName]] 0}]
}
proc S {varName {value {}} } {
uplevel 1 \
[subst -noc {lindex [list [set $varName] [set $varName [list $value]]] 0}]
}
proc G {varName {default ""} } {
upvar 1 $varName var
if { [info exists var] } {return $var}
return $default
}
proc error-args {mesg} {
return -level 2 -code error "wrong # args: should be \"$mesg\""
}
proc list2 {args} {
set outl [list]
foreach item $args {lappend outl $item $item}
return $outl
}And what I consider to be an irritatingly trivial omission from the expr command:
proc ::tcl::mathfunc::sgn {v} {expr {$v > 0 ? 1 : $v < 0 ? -1 : 0}}Also there's my version of the lshift command.
AMG: Here's rmax's clever version of sgn:
proc ::tcl::mathfunc::sgn {v} {expr {($v > 0) - ($v < 0)}}Here's a proc I use for adding new commands to an existing ensemble. It's probably not the best version, but it's handy, and it works where I've needed it.
proc proc-ensemble {args} {
if { ( [llength $args] < 4 || [llength $args] % 3 != 1 ) &&
[llength $args] != 2 } {
error-args "proc-ensemble command ?name args body?..."
}
set args [lassign $args command]
if { [set exists [namespace ensemble exists $command]] } {
set spc [namespace ensemble configure $command -namespace]
set map [namespace ensemble configure $command -map]
# convert "-subcommands" into "-map"
set cmds [namespace ensemble configure $command -subcommands]
if { [llength $cmds] } {
foreach cmd [namespace ensemble configure $command -subcommands] {
dict set map $cmd ${spc}::$cmd
}
namespace ensemble configure $command -subcommands {}
}
} else {
set spc $command
set map {}
}
# validate list-of-procs form
if { [llength $args] == 1 } {
set args [lindex $args 0]
if { [llength $args] % 3 != 0 } {
error-args "proc-ensemble command ?name args body?..."
}
}
# build the new ensemble commands
foreach {name args body} $args {
set lname [string tolower [namespace tail $name]]
if { $body ne "" } {
namespace eval $spc [list proc $name $args $body]
dict set map $lname ${spc}::$name
} elseif { [dict exists $map $lname] } {
dict unset map $lname
}
}
# update the ensemble command map
if { $exists } {
namespace ensemble configure $command -map $map
} else {
namespace ensemble create -command $command -map $map
}
}And now, a proc that does a similar job to regsub, except that the text to be substituted is the value produced by evaluating the supplied script.
proc regcmd {args} {
if { [llength $args] < 3 } {
error-args "regcmd ?switches? exp string script"
}
set parts [list]; set opts [list]; set start 0; set all 0
for {set n 0} {$n < [llength $args]-3} {incr n} {
set arg [lindex $args $n]
switch -exact -- $arg {
-expanded - -line - -linestop - -lineanchor - -nocase
{if {$arg ni $opts} {lappend opts $arg}}
-indices - -inline {if {$arg ni $parts} {lappend parts $arg}}
-start {set start [lindex $args [incr n]]}
-all {set all 1}
default {return -level 1 -code error "bad switch \"$arg\": must be -indices, -inline, -expanded, -line, -linestop, -lineanchor, -nocase, or -start"}
}
}
if { ! [llength $parts] } {set parts "-inline"}
lassign [lrange $args end-2 end] exp strn script
lappend opts -start $start $exp
set out ""
while { [llength [set ranges [regexp -inline -indices {*}$opts $strn]]] } {
set list [list]
foreach range $ranges {
set item [list]
foreach part $parts {
switch -exact -- $part {
"-inline" {lappend item [string range $strn {*}$range]}
"-indices" {lappend item {*}$range}
}
}
lappend list $item
}
lassign [lindex $ranges 0] start stop
append out [string range $strn 0 $start-1][uplevel 1 $script {*}$list]
set strn [string range $strn $stop+1 end]
if { ! $all } break
}
return $out$strn
}