实验二 TCL语言简介
实验目的
学习TCL语言的基本语法。
背景知识
TCL(Tool Command Language)在NS2中主要是用来描述脚本,简单地说就是用来描述要仿真的网络环境和参数设置等。
实验步骤
【安装文书编辑软件PSPad [1]】
在开始学习编写脚本之前,选择一个好用的文字编辑器非常重要。在此推荐一个免费的文字编辑器(PSPad,http://www.pspad.com/en/download.php)给大家。
(1)请切换目录到ns-allinone-2.29/ns-2.29/book/lab2的目录下,执行PSPad的安装文件pspad432inst_en.exe,如图2.1所示。
图2.1 执行PSPad的安装文件
(2)请单击“Next”按钮,如图2.2所示。
图2.2 单击“Next”按钮
(3)请选择“I accept the agreement”单选按钮。
(4)请单击“Next”按钮,则如图2.3所示。此图为PSPad的安装路径,可以选择喜欢安装的位置,不过我们在这里使用内定的路径即可。
图2.3 选择安装位置
(5)请单击“Next”按钮,则如图2.4所示。
图2.4 选择选项
(6)请单击“Next”按钮,则如图2.5所示。
图2.5 开始安装
(7)请单击“Install”按钮,开始进行安装。
(8)安装完成后,则如图2.6所示。
图2.6 安装完成
(9)请单击“Finish”按钮,完成安装,并且开始执行PSPad editor,如图2.7所示。
图2.7 开始执行PSPad editor
【TCL语法练习】
这部分的数据主要是参考文献[2],有兴趣的读者可以到此网站寻找更详细的数据。
1. 变量(Variable)和变量替换(Variable Substituion)
正如其他的程序语言一样,变量可以被想成是用来存储各种类型数据的盒子(Boxes)。这些盒子(或者说是变量),都有各自的名称,而这些名称在我们需要取用或者是变更变量的值(Value)时将会被用到。
在C语言中使用变量时,需要事先的声明,而TCL则不需要事先声明,TCL变量是在第一次使用set的指令来指派变量的值时所产生的。当我们不再需要某个变量时,可以使用unset来取消这个变量。但一般而言,可以不用去做这个动作。
当我们要取用或者是改变变量内的值时,只要在变量名称前加上一个“$”,就可以去取用或者是改变变量内的值,这就是所谓的“变量替换”。
练习1.1:请把下面的程序代码使用文字编辑软件存到2_1_1.tcl。
set foo “john” puts“my name is $foo”
执行方法:($为提示符号)
$ns 2_1_1.tcl
执行结果:
my name is john
说明:
程序的第一行把“john”这个字符串指派给变量foo。程序的第二行使用puts这个指令把后面的字符串表示出来,在表示出来之前,会先把“$foo”先取代成“john”,真正显示出来的字符串是“my name is john”。
练习1.2:请把下面的程序代码使用文字编辑软件存到2_1_2.tcl。
set month 2 set day 3 set year 97 set date "$month:$day:$year" puts $date
执行方法:
$ns 2_1_2.tcl
执行结果:
2:3:97
说明:
在这个练习中,第四行“set date "$month:$day:$year"的$month”会被2所替代;同理,“$day”为3,“$year”为97,这三个变量替代完后会组成一个新的字符串,并通过set指令把新字符串指派给变量date。
练习1.3:(请把下面的程序代码使用文字编辑软件存到2_1_3.tcl)
set foo "puts hi" eval $foo
执行方法:
$ns 2_1_3.tcl
执行结果:
hi
说明:
在这个练习中,foo这个变量包含了一个tcl script,接着,第二行程序中“$foo”会替换成“puts hi”,并且当做eval的参数。eval这个指令主要用于去执行一个tcp script,所以最后“hi”会被表示出来。
2. 表达式(Expressions)
TCL包含许多种类的表达式,如数学表达式、关系表达式等。通常这些表达式都会使用“expr”指令去判断表达式的真假或者去求得表达式的值。
练习2.1:请把下面的程序代码使用文字编辑软件保存到2_2_1.tcl文件中。
set value [expr 0==1] puts $value
执行方法:
$ns 2_2_1.tcl
执行结果:
0
说明:
使用了expr去判断0是否等于1,结果是假,把0存到value变量中,并把结果表示出来。
练习2.2:请把下面的程序代码使用文字编辑软件保存到2_2_2.tcl文件中。
set value [expr 2>=1] puts $value
执行方法:
$ns 2_2_2.tcl
执行结果:
1
说明:
使用expr去判断2是否大于等于1,结果是真,把1存到value这个变量中,并把结果表示出来。
练习2.3:请把下面的程序代码使用文字编辑软件存到2_2_3.tcl文件中。
set value [expr 2+3] puts $value
执行方法:
$ns 2_2_3.tcl
执行结果:
5
说明:
使用了expr去求2+3的结果,并把所得到的5存到value这个变量中,最后把结果表示出来。
3. 指令替换(Command Substitution)
如同变量替换,指令替换可以把“原tcl script执行结果”取代“原tcl script”。
练习3.1:请把下面的程序代码使用文字编辑软件存到2_3_1.tcl文件中。
puts "I am [expr 10 * 2] years old, and my I.Q. is [expr 100 - 25]"
执行方法:
$ns 2_3_1.tcl
执行结果:
I am 20 years old, and my I.Q. is 75
说明:
这个练习中,“[ ]”可以用来达成指令替换,在执行此行“tcl script”时,会先执行“[expr 10*2]”和“[expr 100-25]”,并把结果“20”和“75”取代原本tcl script中“[expr 10*2]”和“[expr 100-25]”的位置,最后再使用puts把此字符串表示出来。
练习3.2:请把下面的程序代码使用文字编辑软件保存到2_3_2.tcl文件中。
set my_height 6.0 puts"If I was 2 inches taller,I would be[expr$my_height+ (2.0/12.0)]feet tall"
执行方法:
$ns 2_3_2.tcl
执行结果:
If I was 2 inches taller, I would be 6.166666666666667 feet tall
说明:
在练习中的第二行,[ ]的内容会先被执行,也就是“$my_height”会被6.0替换,“(2.0/12.0)”的结果会先被计算出来,再和6.0相加,然后用两数相加的结果去替换字符串中的“[expr $my_height +(2.0 / 12.0)]”这个位置,最后才把字符串表示出来。
4. 流程控制(Control Flow)
与其他的程序相同,TCL也提供了一些可以用来控制程序运作流程的方法,包含了if-else、switch、while、for、foreach等指令,这些指令用法请参考下面的实例。
练习4.1:请把下面的程序代码使用文字编辑软件存到2_4_1.tcl文件中。
set my_planet "earth" if {$my_planet == "earth"} { puts "I feel right at home." } elseif {$my_planet == "venus"} { puts "This is not my home." } else { puts "I am neither from Earth, nor from Venus." } set temp 95 if {$temp < 80} { puts "It's a little chilly." } else { puts "Warm enough for me." }
执行方法:
$ns 2_4_1.tcl
执行结果:
I feel right at home. Warm enough for me.
练习4.2:请把下面的程序代码使用文字编辑软件存到2_4_2.tcl文件中。
set num_legs 4 switch $num_legs { 2 {puts "It could be a human."} 4 {puts "It could be a cow."} 6 {puts "It could be an ant."} 8 {puts "It could be a spider."} default {puts "It could be anything."} }
执行方法:
$ns 2_4_2.tcl
执行结果:
It could be a cow.
练习4.3:请把下面的程序代码使用文字编辑软件存到2_4_3.tcl文件中。
for {set i 0} {$i < 5} {incr i 1} { puts "In the for loop, and i == $i" }
执行方法:
$ns 2_4_3.tcl
执行结果:
In the for loop, and i == 0 In the for loop, and i == 1 In the for loop, and i == 2 In the for loop, and i == 3 In the for loop, and i == 4
练习4.4:请把下面的程序代码使用文字编辑软件存到2_4_4.tcl文件中。
set i 0 while {$i < 5} { puts "In the while loop, and i == $i" incr i 1 }
执行方法:
$ns 2_4_4.tcl
执行结果:
In the while loop, and i == 0 In the while loop, and i == 1 In the while loop, and i == 2 In the while loop, and i == 3 In the while loop, and i == 4
练习4.5:请把下面的程序代码使用文字编辑软件存到2_4_5.tcl文件中。
foreach vowel {a e i o u} { puts "$vowel is a vowel" }
执行方法:
$ns 2_4_5.tcl
执行结果:
a is a vowel e is a vowel i is a vowel o is a vowel u is a vowel
5. 程序(Procedures)
在TCL中,也允许用户去自定程序,定义程序基本的语法为proc name params body。其中,name为程序的名称,params是参数列表,body则是程序的主体。定义完程序后就可以像其他任何的tcl指令一样执行了。
练习5.1:请把下面的程序代码使用文字编辑软件存到2_5_1.tcl文件中。
proc sum_proc {a b} { return [expr $a + $b] } proc magnitude {num} { if {$num > 0} { return $num } set num[expr$num* (-1)] return $num } set num1 12 set num2 14 set sum [sum_proc $num1 $num2] puts "The sum is $sum" puts "The magnitude of 3 is [magnitude 3]" puts "The magnitude of -2 is [magnitude -2]"
执行方法:
$ns 2_5_1.tcl
执行结果:
The sum is 26 The magnitude of 3 is 3 The magnitude of -2 is 2
说明:
在程序中定义了两个程序:“sum_proc”和“magnitude”,sum_proc有两个输入参数:a和b,这个程序最主要的工作就是把a和b相加,并把结果返回;另外一个程序magnitude只有一个参数num,这个程序最主要就是去判断num是正数或负数,若是正数则直接返回,若是负数就先与-1相乘再返回。简单地说,magnitude就是在求绝对值。
练习5.2:请把下面的程序代码使用文字编辑软件存到2_5_2.tcl文件中。
proc dumb_proc {} { set myvar 4 puts "The value of the local variable is $myvar" global myglobalvar puts "The value of the global variable is $myglobalvar" } set myglobalvar 79 dumb_proc
执行方法:
$ns 2_5_2.tcl
执行结果:
The value of the local variable is 4 The value of the global variable is 79
说明:
这个练习主要在说明局部变量(local variable)和全局变量(global variable)的观念。要特别说明的是,当程序中需要用到程序外已经定义好的变量前,必须先使用global这个保留字做声明,才能存取程序外已经定义好的变量。
6. 数组(Arrays)
练习6.1:请把下面的程序代码使用文字编辑软件存到2_6_1.tcl文件中。
set myarray(0) "Zero" set myarray(1) "One" set myarray(2) "Two" for {set i 0} {$i < 3} {incr i 1} { puts $myarray($i) }
执行方法:
$ns 2_6_1.tcl
执行结果:
Zero One Two
说明:
程序一开始定义了一个名称为myarray的数组,共有三个元素(element),第一个元素是“Zero”,存放在数组中0的位置;第二个是“One”,存放在数组中1的位置;第三个是“Two”,存放在数组中2的位置。程序最后会把数组的每个元素都表示出来。
练习6.2:请把下面的程序代码使用文字编辑软件存到2_6_2.tcl文件中。
set person_info(name) "Fred Smith" set person_info(age) "25" set person_info(occupation) "Plumber" foreach thing {name age occupation} { puts "$thing == $person_info($thing)" }
执行方法:
$ns 2_6_2.tcl
执行结果:
name == Fred Smith age == 25 occupation == Plumber
说明:
这个练习主要说明存放在数组中的元素不一定非要是数字。在TCL中的数组可以存放数字或者字符串,并且存放的位置不一定要用0,1,2,…这样的编号。
练习6.3:请把下面的程序代码使用文字编辑软件存到2_6_3.tcl文件中。
set person_info(name) "Fred Smith" set person_info(age) "25" set person_info(occupation) "Plumber" foreach thing [array names person_info] { puts "$thing == $person_info($thing)" }
执行方法:
$ns 2_6_3.tcl
执行结果:
occupation == Plumber age == 25 name == Fred Smith
说明:
这个练习与练习6.2最大的不同在于foreach这个循环,在练习6.2中,数组中每个元素存放的位置必须一一列出来,才能表示出数组中每个元素的值。但在这个实例中,使用了array names person_info,这样就可以列出person_info中的name,age,occupation,这在元素很多的时候特别好用。
7. 字符串(Strings)
练习7.1:请把下面的程序代码使用文字编辑软件存到2_7_1.tcl文件中。
set str "This is a string" puts "The string is: $str" puts "The length of the string is: [string length $str]" puts "The character at index 3 is: [string index $str 3]" puts "The characters from index 4 through 8 are: [string range $str 4 8]" puts "The index of the first occurrence of letter \"i\" is: [string first i $str]"
执行方法:
$ns 2_7_1.tcl
执行结果:
The string is: This is a string The length of the string is: 16 The character at index 3 is: s The characters from index 4 through 8 are: is a The index of the first occurrence of letter "i" is: 2
说明:
练习中的“[string length $str]”可以用来显示字符串长度,“[string index $str 3]”可以用来显示字符串中的第四个字母为何(第一个字母的index为0),“[string range $str 4 8]”可以用显示从字符串中第五个到第九个的字母,“[string first i $str]”可以用来显示字母i在字符串中第一次出现时的index值,也就是第一次出现时在第(index+1)个位置。
8. 输出(Output)
练习8.1:请把下面的程序代码使用文字编辑软件存到2_8_1.tcl文件中。
set f [open "/tmp/myfile" "w"] puts $f "We live in Texas. It's already 110 degrees out here." puts $f "456" close $f
执行方法:
$ns 2_8_1.tcl
执行结果:
请使用文字编辑器打开/tmp/myfile,其内容为:
We live in Texas. It's already 110 degrees out here. 456
说明:
一般来说,puts若是没有指定输出装置的话,内定的输出装置是屏幕,但此实例中指定了输出的装置为文件,会把要显示的字符串写入文件内。
【参考文献】
[1] http://www.pspad.com/en/download.php.
[2] http://hegel.ittc.ku.edu/topics/tcltk/tutorial-noplugin.html.