|
名前空間とは? 昔のTcl/Tkは(いまもそうだけど)、 グローバル変数やプロシージャの名前はプログラム全体で共有されていたため、 大きなプログラムでスクリプトを source コマンドでロードしたりすると名前がぶつかって動作が変になったり、 同じ名前のプロシージャがあると勝手に古い方が新しい方に上書きされてしまったりする問題がありました。 そこでTclプログラマは名前がぶつからないように名付け方を工夫したり、 ぶつかっていないかチェックしたりという負担がありました。 そこでえらい人は考えて、Tcl 8.0にバージョンアップするさいに 「名前空間」(namespace) というものを発案し、プログラマが「この名前はこの範囲でだけ有効だよ」 と指定することができるようになりました。歴史的にはこの名前空間は Tcl/Tkの最も有名な拡張のひとつである [incr tcl] から Tcl/Tk本体にいわば「逆輸入」されたもののようです。 Tclの名前空間は、本来の目的から言えば 「変数名やプロシージャ名が知らないうちにぶつかるのを避ける」 ということができればどのように書いてもOKなのですが、 実際にはJavaやPythonのクラスを模した、緩やかなオブジェクト指向パラダイミュを Tcl言語に持ちこむためにも使うことができます。 簡単な、ていうか少し使い古された例ですが、 こんなサンプルを乗せてみます。
namespace eval MonthName {
array set FullName {
1 January 2 February 3 March 4 April 5 May 6 June
7 July 8 August 9 September 10 October 11 November 12 December
}
proc GetByMonth {month} {
variable FullName
if {$month < 1 || $month > 12} {return {}}
return $FullName($month)
}
proc GetByClock {ck} {
variable FullName
set m [string trimleft [clock format $ck -format %m] 0]
return $FullName($m)
}
proc Now {} {
return [GetByClock [clock seconds]]
}
}
puts [::MonthName::Now]
puts $::MonthName::FullName([string trimleft \
[clock format [clock seconds] -format %m] 0])
puts [clock format [clock seconds] -format %B]
あまりに使い古された例なので、ナニをするコードかの説明は省きます。 本題のみということで!
●名前空間をつくるには 名前空間の中で、同じ名前空間のプロシージャを呼び出すには、 普通にプロシージャ名だけで指定すればOKです。上の例では、 NowプロシージャがGetByClockプロシージャを呼び出しています。 名前空間の外、または別の名前空間からある名前空間のプロシージャを呼び出すには、 「::名前空間::プロシージャ名」とします(*1)。上の例では、 puts [::MonthName::Now]がこれを使って外から呼び出しています(*2)。 (*1)名前空間は階層的にすることもでき、その場合は 「::名前空間::名前空間::...::プロシージャ名」となります。 また「カレント名前空間」「親名前空間」などの概念もありますが、 ややこしくなるわりにあまり使わないので、ここでは省略します。 上のFullNameのように、名前空間の中で直接定義されている変数は 「名前空間変数」(Namespace Variable)と呼ばれ、 名前空間内で共有することができます。 名前空間内のプロシージャから同じ名前空間の名前空間変数が見れるようにするには、 プロシージャの冒頭部(*3)で、variableコマンドを使って 「この名前の変数は名前空間変数だよー」と宣言すればOKです。 上ではGetByMonthプロシージャなどがFullNameをvariableコマンドで宣言して使っています。 名前空間の外、または別の名前空間からある名前空間の名前空間変数が見れるようにするには、 「::名前空間::変数名」とします。 puts $::MonthName::FullName([string trimleft \ [clock format [clock seconds] -format %m] 0]) 現在いるコードがどの名前空間にいるかは 「namespace current」コマンドで知ることができます(*4)。 (*3)でなくても構いません。というような感じで充分です。 8.0以降を使っている場合は積極的に名前空間を使うようにしたほうがよいでしょう。
●すげーありがちな落としアナ
namespace eval Hello {
set Message {Hello, World}
proc Init {} {
label .laba -text "Push this button:"
button .cmda -text {Here} \
-command "[namespace current]::ShowDialog"
pack .laba .cmda
bind .laba <Enter> { Hello::MakeRed %W }
bind .laba <Leave> { Hello::MakeNormal %W }
bind . <Key-q> exit
}
proc MakeRed {w} {
$w configure -fg red
}
proc MakeNormal {w} {
$w configure -fg black
}
proc ShowDialog {} {
variable Message
tk_messageBox -message $Message -title hello
}
}
Hello::Init
-commandで指定される文と、
|