总有别人不曾走过的路,总有别人不曾目睹的风景......

Tcl读书笔记(十二)——命名空间

上一篇 / 下一篇  2012-09-05 11:58:32 / 个人分类:Tcl

1. 命名空间与全局命名空间。
   Tcl解释器将所有命令和全局变量分组管理,这些小组称为命名空间,一个命名空间中的命令和变量不会影响另一个命名空间。这些命名空间本身呈树形组织,一个命名空间中的命令可以被另一个命名空间引入。命名空间树的根就是全局命名空间,它包含了没有明确在其他命名空间中创建的所有命令和变量。
   任何已经存在的命名空间中的命令和变量,都可以从命名空间内部或外部创建。在命令或变量的名称前加上命名空间前缀就可以完成这一点,前缀与名称之间用命名空间分隔符分开,分隔符为双冒号。全局命名空间的名称是空字符串,但通常只写一个双冒号就可以表达同样的意思。
   命名空间的主要用途之一是作为创建相关命令包的机制。命名空间可以帮助创建集合命令,把一个命名空间中公共API编组,呈现为公共命令加子命令模式。
2. 在命名空间中处理Tcl脚本。
   namespace eval命令获取命名空间的名称,在指定的命名空间中处理Tcl脚本,返回脚本的结果。如果指定的命名空间不存在,则创建该命名空间。
   proc命令除给出完全限定的情况外,总是创建与当前命名空间相关的命令。注意脚本中的命令如果在指定的命名空间中找不到,就会在全局命名空间中查找。
   要删除命名空间,而不是删除空间中的某个命令,需要使用namespace delete命令。该命令获取命名空间的一个列表,进行删除。命名空间中的变量可由variable命令设置或访问。这个命令获取变量的名称,在当前命名空间中创建变量,如果给出变量的值,就设置变量的值。如果是在该命名空间的一个过程中进行处理,它还会使得指定名称的变量不作限定就在过程中可见。
   namespace eval counter {
   variable num 0
   proc next {} {
   variable num
   return [incr num]
   }
   proc reset {} {
   variable num
   set num 0   
   }
   }
   % counter::next
   > 1
   % counter::next
   > 2
   % counter::reset
   > 0
   % counter::next
   > 1
   注意:总是用variable命令声明变量。在一个命名空间中,如果访问一人没有在该命名空间明确声明的变量,Tcl会首先在全局变量中查找这个名称的变量。如果找到这样的全局变量,Tcl会使用它而不是创建一个命名空间变量。Tcl语言的这种行为初看起来好像不方便,其实这样设计是为了在各个命名空间中,都能更容易地访问预定义的全局变量argv、env等。
   variable命令不能初始化数组的值,但它可以在命名空间中设置变量,允许过程访问它们。也就是说,数组需要单独进行一步初始化操作。
   namespace eval catalog {
   variable entries
   array set entries
   proc add {item} {
   variable entries
   incr entries($item)
   }
   proc getEntries {} {
   variabel entries
   return [lsort [array names entries]]
   }
   proc countInstances {item} {
   variable entries
   return #entries($item)
   }
   }
   % catalog::add apple
   > 1
   % catalog::add orange
   > 1
   % catalog::add apple
   > 2
   % catalog::add banana
   > 1
   % catalog::getEntries
   > apple banana orange
   % catalog::countInstances apple
   > 2
   如果需要生成一个处理回调的脚本,例如lsort命令的-command选项,组件的-command选项,或者after或trace命令,最好是用namespace code命令来生成这些脚本。这会把与脚本有关的代码打包在一起,确保它是在当前命名空间中处理的,而不是在它的调用者的命名空间中处理。
   namespace eval catalog {
   proc save {file} {
   variable entries 
   set f [open $file w]
   puts $f [list array set ::catelog::entries[array get entries]]
   close $f
   }
   proc autocommit {interval file} {
   after $interval [namespace code [list autocommit $interval $file]]
   save $file
   }
   # Save to cataloDB.tcl every 10 seconds.
   autocommit 10000 catalogDB.tcl
   }
3. 操作限定名称
   命名空间中的一切都是经由它的限定名称进行访问的。限定名称由命名空间名称,加上::,再加上命令、变量或子命名空间的局部变量名称组成。限定名称可以是绝对的,以::开头,这个开头的::表示全局命名空间;也可是是相对的,即相对于当前命名空间(不是以双冒号开头)。这与文件系统中的绝对路径和相对路径相似。
   创建限定路径时,可以只用::前缀表示全局命名空间。即::set指向全局命名中的set命令,::env指向全局的env数组。前文创建的catalog命名空间是全局命名空间的直接子命名空间。因此,在全局命名空间中可以使用相对限定名称counter::add调用add过程。从其他命名空间调用时则需要使用绝对限定名称::counter::add。
   要取得限定名称的命名空间部分,应该使用namespace qualifiers命令。该命令返回名称中的命名空间部分。限定名称的局部名称部分可以使用namaspace tail命令取得。
   % namespace qualifiers alpha::beta
   > alpha
   % namespace qualifiers ::alpha::beta::gama
   > ::alpha::beta
   % namespace tail ::aplha::beta::gama
   > gama
   如果把命名空间保存在一个变量中,需要使用下面的形式进行变量交换,否则$变量替换时::会引起错误的解析。
   % set theNS ::alpha::beta
   % set theCommad $theNS::gama
   > can't read "theNS::gama": no such variable
   % set theCommand ${theNS}::gama
   > ::alpha::beta::gama
4. 在命名空间中导出和导入命令
   如果一个命名空间提供某种形式的公共API,那么它应该可以由namespace export命令从命名空间中导出。
   namespace export命令修订和解析与命名空间相关的一个通配符模式列表,将命名空间中与其匹配的命令作为它的公共API导出。
   从命名空间中导出的公共API,可以在另一个命名空间中导入API命令。这样就可以使用导入的命令而无需给出请该命令的完整限定名称。这一操作由namespace import命令管理,该命令获取一个通配符模式列表,以此确定要导入的命令(从其他命名空间的导入命令集合中搜索匹配,选择要导入的命令)。
   namespace eval src {
   proc a {} {return "alpha"}
   proc b {} {return "beta"}
   proc c {} {return "..."}
   namespace export a b
   }
   namespace eval dst {
   namespace import ::arc::*
   proc c {} {return "charlie"}
   expr {* [a] [b] [c] }
   }
   > alpha beta charlie
   namespace import命令有快照的含义:它只导入在调用时处于可导出状态的命令。而且,默认情况下它不会覆盖已经存在的命令,即使这些命令是以前导入的。可以用-force选项强制指定进行覆盖。如果希望从命名空间中移除以前导入的命令,而又不冒意外删除自己创建的命令的风险,应该使用namespace forget命令。
5. 检查命名空间
   namespace current命令获得当前命令空间的名称。
   namespace parent获得当前命名空间的父命名空间的名称。
   namespace children获得当前命名空间的子命名空间的名称。此命令获取可选的命名空间和通配符模式参数,以确定要检查的命名空间,把要检查的命名空间形成列表。
   要查看命名空间中的命令和变量,应使用通用的info子命令,然后指定想查看的命名空间名称以及模式参数。
   如果知道一个命令或变量的非限定名称,可以使用namespace which获得它的完全限定名称。
   当前导出模式的列表,可以不带参数地使用namespace export命令得到。
   namespace origin可查看一个可被导入的命令是由哪个参数命名空间原始导出的。该命令获取一个命令名作参数,返回这个作为参数的命令的原始来源的完全限定名称。如果这个命令就是在本地命名空间中定义的,返回的就是这个命令的完全限定名称。
6. 有关集合的命令
   6.1 基本的集合命令
      创建集合命令的最简单方法是使用namespace export发布一个命名空间组成API命令。在那个命名空间中不带参数地使用namespace ensemble create,就会基于公共API创建集合命令,与命名空间有相同的完整限定名。还可以使用namespace ensemble exists命令测试一个命令是否为集合命令,如果给定的参数是集合命令的名称,就会返回真。
      命名空间中的子命令在应用时可以缩写,但要保证缩写后的子命令是独一无二的。集合命令会调用正确的子命令,但如果缩写后的子命令对应的完整子命令不唯一,集合命令就会抛出匹配错误。
   6.2 在集合命令中设置集合命令
   6.3 控制集合命令的设置
      namespace ensemble命令的create子命令可获取很多选项及其他设置值,大多数选项也可以由namespace ensemble configure子命令控制。
      -subcommands选项明确定义子命列表。
      -prefix选项指定无歧义的命令词头是否可以使用。
      -command选项设置由namespace ensemble create子命令创建的命令是什么。
      -namespace选项让集合命令绑定到的命名空间就是可读的,不过这个选项不能修改设置,而是在创建时自动确定的。
      -map选项包含一个字典,把子命令名映身到一个单词列表。
   6.4 管理集合unknown子命令
      选项unknown是单词的一个列表,当指定的子命令不能解析为集合命令中存在的子命令时,这些单词就组成被调用的命令前缀。
7. 访问其他命名空间的变量
   有时候需要访问其他命名空间的变量,而不仅是当前命名空间,有可能是因为变量表示着一些共享状态,也可能是因为它有特殊的属性。这种访问 有多种方法实现。
   第一,可以使用该变量的完整限定名称。这种方法主要用于从代码中某处访问变量时,或变量是特殊变量,如::env数组时。
   第二,可以把该变量的完全限定名称提供给global、upvar或variable,将变量导入当前命名空间。这种方法常用于要访问的变量数量有限且名称固定时。
   第三,可以使用namespace upvar将一个命名空间中的一组变量导入当前命名空间。当命名是空间代表某些设置的上下文环境,要访问的变量名称中又含变量时特别有用。
8. 名称解析路径的控制
   在一个命名空间中调用命令时,是根据命令名,由一个称为解析的过程查找命令的。以命名空间分隔符::开始的名称总是在全局命名空间中进行查找,而含有命名空间分隔符但不是以分隔符开头的名称,会首先作为相对于当前命名空间的名称进行查找,如果找不到,再作为相对于全局命名空间的名称进行查找。
   对于不包含命名空间分隔符的名称,使用命名空间的解析路径进行解析。这个路径是由namespace path命令控制的命名空间列表,而这个列表的元素总是以当前命名空间开头,以全局命名空间结尾。查找命令时依次检查路径列表中的各个命名空间,查找命令的定义。namespace path命令将路径设定为给定的列表,如果没有给定列表,就返回当前的路径。

TAG: TCL tcl Tcl

kingyao001的个人空间 引用 删除 kingyao001   /   2012-09-05 15:26:55
Tcl语言,你把upvar/uplevel/eval   这三个东西弄懂了,才能用得比较灵活。
 

评分:0

我来说两句

phoenix_lo

phoenix_lo

用户虐我千百遍,我待用户如初恋。

日历

« 2024-05-02  
   1234
567891011
12131415161718
19202122232425
262728293031 

数据统计

  • 访问量: 43808
  • 日志数: 23
  • 建立时间: 2011-06-12
  • 更新时间: 2012-10-30

RSS订阅

Open Toolbar