#

名稱空間(namespace)是一種抽象的容器,它主要的功能是用來區隔同名的變數或程序。想像一下,如果在某個公司裡有一個桃園來的小明和一個台北來的小明,有一天老闆氣呼呼的走進來大喊「小明你明天不用來了」,這樣會造成什麼情況? 可能會讓其中一個無辜的小明嚇出心臟病吧!! 但是如果老闆改說「台北來的小明你明天不用來了」,這樣就不會錯殺良民了。名稱空間的功能是用來防止像這種因為同名而發生錯亂的狀況。它的原則就是,不同名稱空間裡面的變數和程序就算有相同的名稱,也會被當成是獨立的個體,彼此不會互相影響。這一篇文章介紹了名稱空間機制以及使用方法,另外也說明如何把程式拆成多個檔案的方法。

14.1 名稱空間

就像上面說的,名稱空間是一個非常重要的功能,尤其是在大型專案製作或是多人共同開發時更顯得重要,因為很難保証大家不會用到同名的變數或程序。Tcl提供一種階層式的名稱空間管理方法,最頂層是叫做「::」的名稱空間。它是一種全域性質的空間,有時候也叫全域名稱空間,常態下我們會把整個程式會共同用到的變數或程序放在這個空間裡。在全域之下你可以建立任意的名稱空間或更下層的名稱空間,只要遵守「建立下層空間時,上層必定要先存在」的原則就可以了。下面的命令在全域之下建立一個叫dai的名稱空間:



::dai是指頂層之下的dai名稱空間。凡是放在這個名稱空間之下的項目(變數及程序),都會與其它名稱空間的項目有所區隔。如果建立名稱空間時不是由頂層開始,就表示新建立的空間是在目前的空間之下。例如,下面的例子在::dai之下再建立editor及player的空間:



執行完上面的範例執行後,你的程式裡就最少有「::」、「::dai」、「::dai::editor」及「::dai::player」等4個名稱空間了。其中「::」除了用來表示頂層名稱空間外,也用來當做階層的分隔符號。注意哦!! 如果說在執行namespace eval時指定的空間早就存在了,那麼Tcl會把後面大括號的程式當成是在該空間裡執行。

14.2 名稱空間變數

名稱空間變數是專屬某個名稱空間的變數,只要你沒有手動刪除它或是名稱空間沒有被刪除,這種變數會持續存在,直到程式執行結束。例如,下面的程式在::dai空間裡建立了專屬的name及birthday變數:



variable的使用和set命令一樣,但不同的是set主要用來建立一般的變數,而variable則用來建立名稱空間的變數。一但建立成功,在同一個名稱空間裡,你就可以使用一般的方法來取用或設定它們的內容。



如果在不同的空間裡使用name或birthday程式會出錯。例如:



這個例子在::other空間嘗試使用::dai空間裡的birthday變數,結果產生了下面的錯誤訊息,而且程式被中斷了。

can't read "birthday": no such variable
    while executing
"puts $birthday"

如果真的要存取別個空間裡的變數,那你需要在變數的名稱前冠上完整的名稱空間路徑。例如:



請注意程式的第6行,它的意思是birthday在::dai空間之下,由於指出了完整的路徑所以puts就不會找不到birthday變數了。

下面的例子可以用來測試,不同名稱空間裡的變數就算有一樣的名字也是不一樣的個體。



程式的第9~10行順便說明在頂層空間裡存取特定名稱空間變數的方法。

程式輸出:

戴小良
路人甲
戴小良
路人甲

§ 不在名稱空間裡的項目

一般的情況下,如果變數或是程序沒有被放在任何名稱空間裡,那它們都屬於頂層的名稱空間。

14.3 名稱空間程序

把程序定義在某個名稱空間裡的方法如下:



注意程式的第6行,如果在某個空間要呼叫其它空間裡的程序,請使用完整的名稱空間路徑。如果是在同一個空間裡就不用加路徑了。



如果你想在程序裡使用同一個名稱空間之下的變數,請使用下面的方法:



程式的第5~6行用來指明name及birthday是屬於同一層的名稱空間變數。注意!! 這邊不用指定變數的值,若指定的話變數的內容會被重新設定。事實上,不使用variable來指明同層的名稱空間變數,一樣可以使用完整路徑的方法來取用那些變數。但使用variable指明的方式,可以讓接下來的程式少打一些字。

14.4 把程式拆成多個檔案

把程式中獨立的功能拆成獨立的檔案來撰寫是一個好習慣。在Tcl裡你可以用source命令來引入外部的程式檔案。假設下面的程式是一個要被引入的外部檔案:



然後在下面的程式使用source把14-1.tcl引入,還偷用了14-1裡定義的功能。



注意哦!! 在執行14-2時你要想像source會把14-1.tcl裡的程式碼內嵌在第1行的位置。另外一個重點是,上面的兩個檔案要放在同一個目錄裡,或是要指定明確的檔案路徑,不然source會找不到14-1.tcl。

如果說外部檔案和目前程式採用的字元編碼不一樣,那你可以自己指定外部檔案的編碼:



14.5 其它命名空間命令

如果你想知道某個名稱空間有哪些下層空間,可以這樣做:



這個例子會以清單的方式,輸出::dai空間下一層的所有子名稱空間。注意哦!! 只包含下一層而已,更下層的並不會被輸出。

如果你想要知道目前的名稱空間,可以這樣做:



如果你想要知道某個名稱空間是否存在,可以這樣做:



namespace exists判斷::dai空間是否存在,是的話回傳1,不是回傳0。

如果你想要取得某個名稱空間的上層空間,可以這樣做:



如果你想要刪除某個名稱空間,可以這樣做:



當你刪除名稱空間時,所有包含在該名稱空間裡的變數、程序、子空間都會被一起刪除。一般的情況下應該很少用到這個命令才對。

除了上面介紹的操作之外,Tcl還有提供數個名稱空間的功能,有興趣的朋友可以自己去看Tcl的參考手冊。

12 個意見

Ethan Yang | 2010年5月31日 下午6:48

dai大大您好
有幸拜讀您的教學真的是對我學習TCL語言有很大的幫助
謝謝您製作這些教學!

在此想要請教您
14.4(把程式拆成多個檔案)的範例程式
我照著做了
把14-1.tcl跟14-2.tcl都放在C:/abc/下
用命令提示字元下鍵入 tclsh C:/abc/14-2.tcl 卻無效
但是如果把14-2的source改成直接指定路徑 source c:/abc/14-2.tcl的話就可以了
請問大大
放在同一個資料夾的話不是不用指定路徑了嗎?

謝謝大大

dai | 2010年6月3日 上午2:06

hello Ethan Yang,

嗯~你在命令提示字元下輸入 tclsh C:/abc/14-2.tcl 會無效

這是因為source會根據目前的工作目錄pwd來找14-2.tcl這個檔案

也就是說這個pwd是你執行 tclsh C:/abc/14-2.tcl 的位置

我猜測你不是在c:/abc下執行這行命令的

所以程式會找不到14-2.tcl這個檔案

米粒 | 2011年3月24日 下午6:54

下面的例子可以用來測式,不同名稱空間裡的變數就算有一樣的名字也是不一樣的個體
測“式”應該是“試”喔

dai | 2011年3月27日 上午9:30

學弟又開始玩tcl啦?

研究室的學弟都被我涂毒不小 哈~

米粒 | 2011年3月28日 下午7:30

是啊~~影響久遠
不過我老板今天問我怎麼不學python = =

dai | 2011年4月2日 上午8:32

說真的~我最近python的時間比tcl的時間多...呵

匿名 | 2012年7月24日 下午9:37

Dai大大,我有一個小疑問
variable的使用和set命令一樣,但不同的是set主要用來建立一般的變數,而variable則用來建立名稱空間的變數,
我比較不懂的是,那如果再名稱空間中以set建立變數,差別在哪
Ex:
namespace eval ::abc {
set a 1
variable b 1
set c [list 1 2 3]
variable d [list 1 2 3]
}
那這四個變數a、b、c、d差別在哪
麻煩Dai大大啦:D

匿名 | 2012年7月26日 下午3:32

寫得很好...
請問有進階版的嗎?
不過玩了python之後就不太想寫tcl了...
未來tcl會被python取代吧~

陳貴鑫 | 2013年1月3日 下午8:06

讀到這裡會讓我聯想到 C++ 的名稱空間
8.6 的 Tcl 好像有添加物件導向的特性
未來大大物件導向的用法嗎
(感覺物件導向已經人人皆知了 只是看大大會不會想教怎麼宣告)
順便回一下樓上 python 不太可能會取代 tcl/tk 畢竟人家 python 還是轉換成 tcl/tk
還有不少eda工具像是 modelsim 也是用 tcl/tk 開發的
tcl/tk 我現在學起來 發現真的適合拿來快速開發

P.S.第一個google到的中文 tcl 網頁是這個教學網站耶~ 多希望大大可以熱情地繼續寫下去

Weison | 2014年8月27日 下午9:55

你好~想請教一下~我想要將透過執行外部tcl 並將執行結果寫入一個文字檔 請教應該如何寫入?
source test.tcl 後面是否還要加入甚麼參數呢

dai | 2014年8月28日 上午11:03

在test.tcl裡寫類似這樣的程式
set fd [open "c:/a.txt" w]
puts $fd "text 12345"
close $fd

Weison | 2014年8月28日 下午2:54

to dai 大
本來是想從source 這個方式尋求餵資料的方式 ,但後來還是從執行的那隻tcl去開檔填值,就跟你上面提供的方法一樣的,感謝!!!

留下您的意見

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates