3.4.1 簡介
TCL (Tool Command Language)在ns2中主要是用來描述腳本的,簡單的說就是用來描述要模擬的網路環境和參數設定等。
p.s. 在練習底下的範例時,請先把程式碼存放到檔案內,然後使用NS2去執行即可。
3.4.2 變數(variable)和變數替換(variable substituion)
如同其他的程式語言一樣,變數(variables)可以被想成是盒子(boxes),是用來儲存各種類型的資料的。這些盒子或者說是變數,都有各自的名稱,而這些名稱在我們需要取用或者是變更變數的值(value)時會被用到。
C語言在使用變數時,需要事先的宣告,而TCL則不需要事先宣告。TCL變數是在第一次使用set的指令來指派變數的值時所產生的。而當我們不再需要某個變數時,可以使用unset來取消這個變數,但一般而言,我們可以不用去做這個動作。
而當我們需要取用或者是變更變數內的值時,只要在變數名稱前加上一個$的符號就可以去取用或者是變更變數內的值了,這就是所謂的變數替換。
Example 1.1: (請把下面程式碼存到ex1_1.tcl)
set foo “john”
puts “my name is $foo”
執行方法:
ns ex1_1.tcl
執行的結果:
my name is john
說明:
程式第一行:把”john”這個字串指派給變數foo。程式的第二行:使用puts這個指令把後面的字串秀出來,而在秀出來之前,會先把$foo先取代成john,所以真正被秀出來的字串是my name is john。
Example 1.2: (請把下面程式碼存到ex1_2.tcl)
.set month 2
set day 3
set year 97
set date "$month:$day:$year"
puts $date
執行方法:
ns ex1_2.tcl
執行的結果:
2:3:97
說明:
在程式中的第四行set date "$month:$day:$year"中,$month會被2所替代,同理,$day會為3,$year為97,這三個變數替代完後會組成一個新的字串,並藉由set指令把新字串指派給變數date。
Example 1.3: (請把下面程式碼存到ex1_3.tcl)
.set foo "puts hi"
eval $foo
執行方法:
ns ex1_3.tcl
執行的結果:
hi
說明:
在這個範例中,foo這個變數包含了一個tcl script,接著第二行程式中$foo會替換成”puts hi”,並且當作是eval的參數。eval這個指令主要是用於去執行一個tcp scrip,所以最後hi會被秀出來。
3.4.3 表示式(expressions)
TCL包含許多種類的表示式,如數學表示式、關係表示式等。通常這些表示式都會使用expr這個指令去判斷表示式的真假或者去求得表示式的值。
Example 2.1: (請把下面程式碼存到ex2_1.tcl)
set value [expr 0==1]
puts $value
執行方法:
ns ex2_1.tcl
執行的結果:
0
說明:
在範例中使用了expr去判斷0是否等於1,結果是假,所以把0存到value這個變數中,最後並把結果秀出來。
Example 2.2: (請把下面程式碼存到ex2_2.tcl)
set value [expr 2>=1]
puts $value
執行方法:
ns ex2_2.tcl
執行的結果:
1
說明:
在範例中使用了expr去判斷2是否大於等於1,結果是真,所以把1存到value這個變數中,最後並把結果秀出來。
Example 2.3: (請把下面程式碼存到ex2_3.tcl)
set value [expr 2+3]
puts $value
執行方法:
ns ex2_3.tcl
執行的結果:
5
說明:
在範例中使用了expr去求得2+3的結果,並把所得到的5存到value這個變數中,最後並把結果秀出來。
3.4.4 指令替換(command substitution)
就如同變數替換一樣,指令替換可以把”原tcl script執行結果”取代”原tcl script”。
Example 3.1: (請把下面程式碼存到ex3_1.tcl)
puts "I am [expr 10 * 2] years old, and my I.Q. is [expr 100 - 25]"
執行方法:
ns ex3_1.tcl
執行的結果:
I am 20 years old, and my I.Q. is 75
說明:
在範例中,square bracket(也就是[])可以用來達成指令替換,所以在執行此行tcl script時,會先去執行[expr 10*2]和[expr 100-25]並把結果20和75取代原本tcl script中[expr 10*2]和[expr 100-25]的位置,最後在使用puts把此字串秀出來。
Example 3.2: (請把下面程式碼存到ex3_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 ex3_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)]這個位置,最後才把字串秀出來。
3.4.5 流程控制(control flow)
跟其它的程式相同,TCL也提功了一些方法可以用來控制程式運作的流程,這包含了if-else、switch、while、for、foreach等指令,這些指令用來請參考下面範例。
Example 4.1: (請把下面程式碼存到ex4_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 ex4_1.tcl
執行的結果:
I feel right at home.
Warm enough for me.
Example 4.2: (請把下面程式碼存到ex4_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 ex4_2.tcl
執行的結果:
It could be a cow.
Example 4.3: (請把下面程式碼存到ex4_3.tcl)
for {set i 0} {$i < 5} {incr i 1} {
puts "In the for loop, and i == $i"
}
執行方法:
ns ex4_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
Example 4.4: (請把下面程式碼存到ex4_4.tcl)
set i 0
while {$i < 5} {
puts "In the while loop, and i == $i"
incr i 1
}
執行方法:
ns ex4_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
Example 4.5: (請把下面程式碼存到ex4_5.tcl)
foreach vowel {a e i o u} {
puts "$vowel is a vowel"
}
執行方法:
ns ex4_5.tcl
執行的結果:
a is a vowel
e is a vowel
i is a vowel
o is a vowel
u is a vowel
3.4.6 程序(procedures)
在TCL中也允許使用者去自定程序,定義程序基本的語法為: proc name params body,其中name為程序的名稱,params是參數列表,body則是程序的主體。定義完程序後就可以像任何其它的tcl指令一樣執行。
Example 5.1: (請把下面程式碼存到ex5_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 ex5_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就是在做絕對值。
Example 5.2: (請把下面程式碼存到ex5_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 ex5_2.tcl
執行的結果:
The value of the local variable is 4
The value of the global variable is 79
說明:
這個範例主要在說明區域變數(local variable)和全域變數(global variable)的觀念。這樣要特別說明的是當程序中有需要用到程序外已經定義好的變數前,必需先使用global這個保留字做宣告,才能存取程序外已經定義好的變數。
3.4.7 陣列(arrays)
Example 6.1: (請把下面程式碼存到ex6_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 ex6_1.tcl
執行的結果:
Zero
One
Two
說明:
程式一開始定義了一個陣列,名稱為myarray,共有三個元素(element),第一個為”Zero”,存放在陣列中0的位置;第一個為”One”,存放在陣列中1的位置,第一個為”Two”,存放在陣列中2的位置,程式最後是把陣列每個元素都秀出來。
Example 6.2: (請把下面程式碼存到ex6_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 ex6_2.tcl
執行的結果:
name == Fred Smith
age == 25
occupation == Plumber
說明:
這個範例主要是在說明,存放在陣列中的元素不一定要是數字,在TCL中的陣列是可以存放數字或者是字串,並且存放的位置不一定要用0、1、2...這樣的編號。
Example 6.3: (請把下面程式碼存到ex6_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 ex6_3.tcl
執行的結果:
name == Fred Smith
age == 25
occupation == Plumber
說明:
這個範例跟6.2最大的不同是在foreach這個迴圈,在6.2中,陣列中每個元素存放的位置必須一一列出,才能秀出陣列中每個元素的值,但是在這個範例,使用了array names person_info,使用的這樣的方法就可以列出person_info中的name、age、occupation,這個在元素很多的時候特別好用。
3.4.8 字串(strings)
Example 7.1: (請把下面程式碼存到ex7_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 ex7_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)個位置。
3.4.9 輸出(output)
Example 8.1: (請把下面程式碼存到ex8_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 ex8_1.tcl
執行的結果:
請使用文字編輯器打開/tmp/myfile,其內容為:
We live in Texas. It's already 110 degrees out here.
456
說明:
一般來說,puts若是沒有指定輸出裝置的話,內定的輸出裝置是螢幕,但此範例中有指定輸出的裝置為檔案,所以會把要秀出的字串寫入檔案内。
3.4.10 副程式的寫法(procedure)
# 定義一個叫做 show 的 procedure
proc show {}
{
... # 副程式內容 ...
}
# 範例: (計算x階乘的procedure) proc fac {x}
{
if {$x < 0}
{
error "Invalid argument $x: must be a positive integer"
}
elseif {$x < = 1}
{
return 1
}
else
{
return [expr $x * [fac [expr $x-1]]]
}
}