过程与作用域
一个过程创建好后,就可以象 Tcl 内建命令一样直接使用。
Tcl8.0 以后的版本增加了名字空间(name space),它为过程和变量提供了新的作用域。
1. proc—过程定义命令
使用 proc 命令定义过程:
proc procName { var1 var2 ... } { body
}
proc 命令有三个参数:
-
procName 是定义的过程名字;
-
{var1 var2 ...}是输入、输出参数列表;
-
body 是过程执行命令体。 body 的界定大括弧和 if 等命令的命令执行体遵循相同的规则和注意事项。
- 可以使用 return 命令在需要的时候返回调用程序。
- 使用过程的时候,不一定输入所有的参数值。过程的输入参数可以有默认值。默认值由{默认参数名 默认值}指定。如果调用过程时没有指定这些参数的值则会使用其默认值,否则使用输入值来替代默认值。在使用默认参数的时候要注意,如果默认参数之后还有非默认参数,则在调用此过程的时候,默认参数的值也要求输入,否则会出错。
带有默认参数的过程定义示例:
proc Test {a {b 7} {str "hello World"}} {
puts "$str"
return [expr
}
Test 10
=> Hello World
70
Test 10 5 "Call Test"
=> Call Test
50
如果参数列表中最后一个参数是 args 的话,则过程可以接收可变数目的输入参数。当调用过程时,除了指定参数以外的参数值都被 args 接收。如果参数列表中只有 args 一项,则 args 接收所有输入参数值。
proc vParam {args} {
puts "input values are : $args"
}
vParam
=> input values are :
vParam 1 "Hello" 2 3 "End"
=> input values are : 1 Hello 2 3 End
2. 作用域
2.1 过程的作用域
默认情况下,过程名具有单一的作用域即全局作用域。所以可以在脚本的任何地方使用一个过程(使用之前,过程必须已经定义)。可以通过名字空间来限定过程的作用范围。如可以通过名字空间命令设置一个过程可以让外部调用,也可以设置过程不为外部所使用。
过程的定义可以嵌套,低层定义的过程只有在上层过程被执行后才能生效。
proc one {} {
puts "I'm one"
proc two {} {
puts "I'm two"
}
puts "Call two in one:"
two
}
two
=> invalid command name "two"
one
=> I'm one
Call two in one:
I'm two
two
=> I'm two
2.2 变量的作用域
对于变量而言,根据其作用域可分为全局变量和局部变量。在所有过程之外定义的变量为外部变量,即全局变量,它的作用域为从开始定义到执行结束,除非中间有显式取消其定义。在一个过程体内定义的变量为内部变量,即局部变量,局部变量的作用域只限于过程内部使用,在此过程外面不能使用这些变量。在一个过程内部,即可以使用自身的局部变量,又可以使用全局变量,但是全局变量在过程内部不会自动可见,需要通过 global 命令来事先声明。因为作用域不同,所以过程中的变量可以与全局变量、其他过程中的变量有相同的名字。
在过程定义中的输入、输出参数列表中的参数为过程的内部参数。
set a 5
set b -8
set c 10
proc P1 {a} {
set b 42
global c
puts "$a"
puts "$b"
puts "$c"
}
P1 $b
=> -8
42
10
全局变量的定义不一定要在过程外完成,可以在任何一个过程中完成,其效果是将一个局部变量的作用域进行了扩展。但是在引用全局参数的值之前全局参数应首先已被赋值。
另外当一个过程中要使用与局部变量相同名字的参数时,用global 命令就要小心:你不能在定义完成一个局部变量后再用 global 命令使用同名的全局参数,这会出错。
proc P2 {} {
global var1
set var1 100
puts "var1=$var1" #在P2内定义全局变量
}
proc P3 {} {
global var1
puts "var1=$var1"
}
proc P4 {} {
global var2
put $var2
}
proc P5 {} {
set a 30
global a
puts "a=$a"
}
P2
=> var1=100
P3
=> var1 = 100
P4 #试图使用不存在的全局变量
=> can't read "var2:no such variable"
P5 #在定义局部变量后视图引用同名全局变量
=> variable "a" already exists
还有另外一种方式来直接使用全局变量即用双冒号( ::),见下例:
proc test {} {
puts "The value of global variable var1 is $::var1"
}
set var1 20
=> 20
test
=> The value of global variable var1 is 20
2.3 upvar命令
upvar 命令的作用与 global 的作用相似, upvar 通过“引用”来使用上层过程中的变量,它传递的是参数名而非值。
语法:
- upvar ?level? otherVar1 myVar1 ?otherVar2 myVar2 ... ?
upvar 命令将 myVar1 定义为 otherVar1 的一个引用, otherVar 是有 level指定的本过程调用栈中的向上 level 层的变量。当定义好之后,本过程就可以通过 myVar 参数来使用 otherVar 参数。
level 有两种表示方式:
- 当 level 为一个整数值时,表示从当前作用域向全局作用域上溯到level 层作用域。比如 level 为 1 时,代表调用过程作用域, level 为 2 时为上一层作用域。
- 如果 level 是一个”#”跟一个整数,则表示从全局作用域向当前作用域下溯。比如 level 为#0,则表示全局作用域,此时的作用同 global 命令。
上面介绍 global 的时候提到,如果过程已经用 set 命令定义了一个参数,此时用 global 命令使用全局变量中相同名字的参数就会出错,报告参数已经存在。而通过 upvar命令却可以作到这一点, upvar 命令通过一个不同的参数名来引用全局变量。
2.4 rename 命令
rename 命令可以用来更改命令名,这些命令包括 TCL 自带的内建命令和读者自己定义的过程。
语法:
- rename oldFuncName newFuncName
如有一个命令名为 old,则通过命令:
- rename old new
将命令名改为了 new。如果以后要调用原来的命令就不能用 old,只能用 new。如果新命令名 newFuncName 是空字符串{},则 rename 命令此时的作用是取消一个命令。
2.5 特殊变量
当 Tcl shell 被调用的时候, tclsh 或 wish 会设定一些变量,如命令行参数和环境变量数组。在编程时,可以使用命令行参数和环境变量数组传递参数信息。
2.5.1 命令行参数
命令行参数是在 Tcl shell 被调用时定义/初始化的。命令行参数有:
-
argc :命令行参数的数目,不包括执行脚本的名字
-
argv0 :脚本名
-
argv :命令行参数列表
下面示例中,先编写一个 Tcl 脚本文件test.tcl,然后调用 Tcl shell tclsh 来运行此脚本文件打印命令行参数信息。第一次没有输入任何命令行参数,第二次输入了 5 个命令行参数。
步骤一:编写test.tcl脚本文件
puts "The number of command line arguments is:$argc"
puts "The name of command the scriptis is:$argv0"
puts "The command line arguments are:$argv"
示例一:运行test.tcl脚本文件,不添加命令行信息
tclsh test.tcl #不添加命令行信息
=> The number of command line arguments is:0 #表示命令行参数的数目为0
The name of the scriptis:test.tcl #表示脚本名
The command line arguments are: #表示命令行参数列表
示例二:运行test.tcl脚本文件,添加命令行信息
tclsh test.tcl a b c d e #添加命令行信息
=>
The number of command line arguments is:5 #表示命令行参数的数目为0
The name of the scriptis:test.tcl #表示脚本名
The command line arguments are:a b c d e #表示命令行参数列表
2.5.2 env - 环境变量数组
TCL 提供了一 事先就定义好的全局环境变量数组,这个数组叫做 env。读者可以通过环境变量数组元素的名字来使用各元素的值。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端