关于一段生成指定范围不重复随机数的TCL代码的疑问
TCL指定范围随机数生成代码的疑问解答
原代码与背景
相关代码及myRand过程定义如下:
主代码
set r -1; # Some value that definitely isn't in the sequence for {set i 1} {$i < 31} {incr i} { upvar 0 fnode($i) fnod($i) while {$r == [set r [myRand 1 20]]} { # Empty body } set fnod($i) $r; # Random number is generated between 1 to 20 }
myRand过程定义
proc myRand {min max} { set range [expr {$max - $min + 1}] return [expr {$min + int(rand() * $range)}] }
疑问解答
1. upvar 0 fnode($i) fnod($i)的作用及未定义数组却使用upvar的问题
这行代码完全多余且存在逻辑错误:
upvar 0 a b的作用是将当前作用域的变量a链接为别名b,但这里fnode($i)是数组元素格式,fnod($i)同样是数组元素格式的变量名,Tcl的upvar不支持将数组元素链接到这种格式的变量。- Tcl的数组是动态创建的,第一次赋值时会自动生成数组,所以
fnode未提前定义不影响,但这行upvar毫无意义——直接使用fnode($i)赋值即可,不需要别名。
2. 为何能保证30个随机数中有20个不重复?
这段代码根本无法保证该效果,原代码逻辑存在错误:
- 代码中的
while循环仅确保当前生成的随机数和上一次的结果不重复,避免连续重复,但完全允许非连续的重复值。比如第一次生成1,第二次生成2,第三次仍可生成1,无法实现20个不重复的要求。 - 若要生成1-20范围内无重复的随机数,正确做法是先创建包含1-20的列表,用
random shuffle打乱后取元素;若需要30个(超出范围),可循环取打乱后的列表元素。
3. 在TclTutor 3.0b6中运行报错的原因
报错信息bad variable name "fnod(1)": upvar won't create a scalar variable that looks like an array element的核心原因是:
upvar默认操作标量变量,而fnod($i)带括号的格式会被Tcl识别为数组元素,upvar不允许创建这种形式的标量变量,因此抛出错误。- 解决方法:直接删除这行
upvar代码,将set fnod($i) $r改为set fnode($i) $r即可正常运行。
内容的提问来源于stack exchange,提问作者Theoretical Physics
相关产品推荐
相关产品推荐

