3
7
2015
2

Tcl 现代方法:变量与作用域

Tcl 是个很老很老的语言了,而且最让人叹惋的是,这语言一开始就饱受关注,然后就被广泛使用了,从此作出大的改动已经不太可能了。变量与作用域问题就是 Tcl 时代限制在这方面的体现。

Tcl 从本质上说,是个动态作用域语言,下面的代码会报错:

set a 100
proc foo {} {
    puts $a ;# undefined variable 'a'
}

然而,Tcl 又有一大堆奇妙的东西,可以让你访问那些变量:

set x 50

proc bar {_a} {
    global x
    upvar $_a a
    set a [expr $a+$x]
}

proc foo {} {
    set a 100
    bar a
    puts $a ;# 输出 150
}

这实际上就是陈梓瀚嘲笑过的 pass by name。bar 里面用到了两个特殊的工具:global 和 upvar,可以访问另一个 level 的变量。可以注意到,这里的 bar 把 foo 的局部变量修改了,我们传递的不是 $a (a 的值),而是 "a" (a 这个变量的名称),而 bar 使用 upvar 将 a 这个变量绑定到 bar 的局部变量名称 a(不应与 foo 的 a 混淆,实际上这里可以叫任何名字),从而实现了完全接管 foo 的 a 的目的。这里的 global 可以看作 upvar 的一个语法糖,把全局的一个变量 x 绑定到本地变量 x。

我们已经知道,如果 global 的对象变量不存在,则会创建一个变量;同理,upvar 也会这样做。

set x 10

proc foo {} {
    global x y
    set y 10
    expr $x+$y
}

proc bar {} {
    upvar g local
    set g 10
    return $g
}

这可以与 C 语言类比,在 C 中要让另外一个函数修改一个变量,一般使用 & 传递指针,如 &a,在 Tcl 中也类似,只不过用的是单纯的一个名字。

这样,我们看到,Tcl 居然好像也可以看作有一点词法作用域(虽然需要你手动做很多事情)。除了访问另外一个函数中的局部变量,我们也可以直接使用命名空间:

set x 50

proc foo {} {
    puts $::x
}

此处 :: 表示全局。

传统上,创建与读写变量统一使用 set,在读取全局变量时使用 global,但我们既然在讲现代方法,就不能囿于上古做法。

随着程序的增大,必然出现了组织代码的需求,命名空间随即引入,同时引入了 variable 命令。我们很快会注意到 variable 和「变量」有密切联系,同时会自然地生发问题:「variable 和 set 有何区别?」

首先看一个示例,假设我们现在需要在代码中读取应用程序名称,很自然地我们使用了命名空间。

namespace eval Config {
    set AppName "Demo"
}

这是我们一开始会想到的做法。实际上 Config::AppName 也可以访问,这没什么问题。不过,这里的 set 看上去不像是定义,而像是普通的程序逻辑,所以可以使用 variable 替代之。

namespace eval Config {
    variable AppName "Demo"
}

这样看上去就比较漂亮了(个人看法)。同时,variable 还有 set 绝对无法办到的事情,这个用途与 upvar 和 global 类似:

proc Config::foo {} {
    variable AppName
    puts $AppName ;# 输出 Demo
}

也就是说,variable 可以把一个命名空间的变量引入到当前的作用域中,默认情况下是当前函数所在的命名空间,如此处的 Config。当然,和 global 一样,variable 也能直接从函数里面创建外面命名空间的变量。

和普通命名空间不同,在 TclOO 中,成员变量无需多次 variable:

oo::class create CTest {
    variable m_hello
    constructor {} {
        variable m_hello "Hello!" ;# 初始化,这里也可以使用 set
    }
    method foo {} {
        puts $m_hello ;# 注意这里没有使用 variable
    }
}

总的来说,variable 算是 upvar 和 set 的杂交产物。

现代 Tcl 最佳实践:

  1. 尽量不使用 upvar 和 global
  2. 如大量引用全局变量,使用 global;否则使用 ::
  3. 如大量引用命名空间变量,使用 variable;否则使用 quantified name
  4. 定义命名空间变量时尽量使用 variable,除非是程序逻辑
  5. 在类中不要使用 variable,仅用来定义和初始化(初始化也可以使用 variable)
Category: 技术 | Tags: tcl tcl 现代方法 | Read Count: 3689
WB HS Model Paper 20 说:
Aug 18, 2022 07:35:20 PM

The West Bengal Council of Higher Secondary Education, or WBCHSE, has prescribed the Class 11 Important Model Question Paper for 2023. The Board, which was established in 1975, works to promote higher secondary education in the State of West Bengal. In the same year, The Board Attained Autonomous Status. This Autonomous Body Conducts a Comprehensive Study to Design a Key Model Question Paper for the Year 2023. WB HS Model Paper 2023 The Jury's Subject Matter Experts will Select the Subjects in Light of Students' Psychological Potential in Order to Compete with the Rapidly Changing World. Students must practise and research the WBCHSE HS Important Model Question Paper from a year ago in order to pass the West Bengal 11th grade exams in 2023.

milan 说:
Jan 12, 2023 01:31:42 PM

Modules in the Tcl programming language provide a mechanism for organizing related Tcl code into library files. Each module typically contains a number of related procedures, and a module muscle and joint pain can be imported into a Tcl script or program to make the procedures it contains available. One advantage of using modules is that they can help to make Tcl code more readable and maintainable by encapsulating related code in a single place. Modules can also make it easier to reuse Tcl code in different programs or scripts.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Powered by Chito | Hosted at is-Programmer | Theme: Aeros 2.0 by TheBuckmaker.com