Generating a unique name

Marco Maggi - The procedure presented in this page will generate a number of unique fully qualified names. All the names referes to entities in the "::tcl::tmp" namespace. Invoking the procedure with no arguments will return a name:

namespace import ::tcl::tmp::unique_name
set name [unique_name]

# $name -> ::tcl::tmp::123

giving variable names on the command line will cause the procedure to store a unique name in each of them:

namespace import ::tcl::tmp::unique_name
unique_name name1 name2 name3

# $name1 -> ::tcl::tmp::1
# $name2 -> ::tcl::tmp::2
# $name3 -> ::tcl::tmp::3

This procedure is useful when a script needs to store data in a static variable (a global or namespace variable), and to hand the variable name as argument to some procedure making the data available to other modules.

Nothing prevents us to use the generated names as procedure or namespace names, or simply as unique identifiers.


namespace eval ::tcl::tmp {
    variable        global_counter 0

    namespace export unique_name
}

proc tcl::tmp::unique_name { args } {
    variable        global_counter
    set pattern        "[namespace current]::%s" 
    set result        {}

    set num [llength $args]
    set num [expr {($num)? $num : 1}]

    for {set i 0} {$i < $num} {incr i} {
        set name [format $pattern [incr global_counter]]
        while {
            [info exists $name] ||
            [namespace exists $name] ||
            [llength [info commands $name]]
        } {
            set name [format $pattern [incr global_counter]]
        }
        lappend result $name
    }

    if { [llength $args] } {
        foreach varname $args name $result {
            uplevel set $varname $name
        }
    }
    return [lindex $result 0]
}

Ro: I do it like this: set name x[clock micro]


PYK 2016-01-20: Here's a snippet that produces a string of random letters, 24 in this case:

proc uniqid {} "lindex [string repeat {[format %c [expr {entier(rand() * 26 + (int(rand()*10 > 4 ? 97 : 65)))}]]} 24]"

And here is one that produces an a string of printable characters that, when interpreted as a base-68 number occupies approximately, but no more than 256 bits:

proc uniqid {} "lindex [string repeat {[lindex {# % + - ^ = 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z} [expr {entier(rand() * 68)}]]} 42]"

A version of uniqid, called randprint_256, that uses /dev/random is available in ycl math rand. Another command, randprint_script accepts a random number generator and a count, and generates a script similar to uniqid.

See also