2. 示例

表 1. 差异

VB6Tcl/Tk 8.3
注释/差异

dim a as integer
dim b as integer
a=1 : b=0

set a 1; set b 0

每行多个命令的分隔符。Tcl 使用分号。通常认为每行多个命令是一种不好的形式,但分号也用于实现部分行注释,因此此处对其进行说明。

' this is a whole line

# this is a whole line
                       

整行注释。两种语言都不需要在注释标记后加空格。

dim a as integer
a=1 'this is a partial-line comment
                       

set a 1 ;# this is a partial-line comment
                       

部分行注释。请注意分号,它用作该行上的另一个命令。

dim s as string
s="/data/docs/vb6_to_tcl.htm"
                       

set s {/data/docs/vb6_to_tcl.htm}
                       

使用花括号引用的字符串的赋值。大多数 Tcl 替换 *不* 在花括号引用的字符串中完成。如果字符串包含变量或其他需要替换的项,则这些替换将被延迟,但可能会在稍后进行。通常由实现控制结构的命令(如“if”或“while”)执行此操作。 一旦您开始熟悉 Tcl,请尝试彻底理解此过程,因为它对于在 Tcl 中变得“出色”非常重要。

(No equivalent)
                       

set s "/data/docs/vb6_to_tcl.htm"
                       

带引号的字符串的赋值。所有 Tcl 替换(变量、命令、反斜杠)都可以在带引号的字符串中使用。

(No equivalent)
                       

set s /data/docs/vb6_to_tcl.htm
                       

未带引号的字符串的赋值。所有 Tcl 替换(变量、命令、反斜杠)都可以在未带引号的字符串中使用。解释器只是将该字符串作为 set 命令中的第三个词(set 命令的第二个参数)。如果字符串中没有空格或某些其他字符,则此方法有效。请谨慎使用,尤其是在处理用户输入的任意数据时。

dim s as string
s = vbCrLf &"Free software is not just" &vbCrLf _
  &"about being 'free of charge'" &vbCrLf _
  &"but about freedom to create" &vbCrLf _
  &"and use the best possible tools." &vbCrLf
                       

set s {
Free software is not just
about being 'free of charge'
but about freedom to create
and use the best possible tools.
}
                       

多行字符串的赋值。请注意 VB 中更混乱的语法,这使其比 Tcl 代码更难阅读。

dim s as string
dim t as string
s = trim(t)
                       

set s [string trim $t]

                       

函数返回值的赋值。此 set 命令的第三个单词用方括号括起来。这意味着它本身是一个要执行的命令,其结果将代替它作为 set 命令的第三个单词。

dim s as string
dim t as string
s = lcase(trim(t))

                       


set s [string tolower [string trim $t]]

                       

函数的函数的赋值。

dim x as double
dim y as double
x = (y + 10) * 5
                       

set x [expr {($y + 10) * 5}]

                       

数学表达式结果的赋值。Tcl 解释器依赖 expr 命令来评估数学或逻辑表达式。 许多其他命令(如“if”或“while”)也在它们的实现中依赖 expr。显式使用时,应将 expr 传递一个包含表达式的字符串的单个参数(如此处所示)。 在您只想将某个增量添加到变量的简单情况下,这可能会变得很麻烦。请尝试改用 incr 命令。

dim s as string
s = s &"more text"
                       

append s {more text}
                       

附加到现有字符串。这是 VB 中最慢的操作之一,但在 Tcl 中通常非常快。速度在这里很重要,因为它通常在循环或复合循环中完成。

dim s as string
dim t as string
dim u as string
s = "I'll ask " & t & " to email " & trim(u) & " with the price"                    

set s "I'll ask $t to email [string trim $u] with the price"
                   

通过替换构建字符串。

print "hello"
                       

显示 hello。
打印到控制台(VB 实际上打印到窗体或调试窗口)。

sub my_sub (byval a as integer, byval b as string)

    debug.print "I'll ask " & b

end sub

function my_function (byval a as integer, _
        optional byval b as string = "Mark") _
        as string

    my_function = "I'll ask " & b

end function
                       

proc my_sub {a b} {

    puts "I'll ask $b"

}

proc my_function {a {b Mark}} {

    return "I'll ask $b"

}

                       

过程定义。请注意,VB 对 subs 和 functions 使用单独的语法。 Tcl 使用 proc 命令来定义两者。 proc 本身是一个普通的 Tcl 命令,其执行方式与其他任何命令一样。 它的第一个参数是新过程的 Tcl 参数列表。 它的第二个参数是一个包含新过程主体的长字符串(实际的 Tcl 脚本)。 重要提示: Tcl 在几乎所有操作中都区分大小写,包括对命令名称和变量名称的所有引用,以及(默认情况下)字符串数据比较。 因此,调用 Proc 会导致错误(大写 P),调用My_Sub或引用变量Bmy_sub中(b 定义为小写)也会导致错误。



dim i as integer
if i < 0 then i = 0 else i = i - 1




                       

if {$i < 0} {set i 0} {incr i -1}

# alternate form
if {$i < 0} then {set i 0} else {incr i -1}

# another alternate form
if {$i < 0} then {
    set i 0
} else {
    incr i -1
}                      

“if”条件执行。 Tcl “if”命令会忽略可选关键字“then”和“else”(如果存在)。 由于两个代码块都只是字符串,因此可以将它们括在花括号中并按如上所示进行格式化。 为了避免语法错误,还可以将任何非平凡的测试表达式括在花括号中。 这样,替换(例如此处的 $i)将被延迟,直到“if”命令将测试表达式传递给表达式分析器。

dim i as integer
i = 1
while i < 2000
    i = i * 2
wend

'alternate form
i = 1
do while i < 2000
    i = i * 2
loop                       



set i 1
while {$i < 2000} {
    set i [expr {$i * 2}]
}


                       

“while”循环。 这类似于 Tcl 的“if”命令,因为它将测试表达式作为其第一个参数,后跟一段代码字符串。







dim i as integer
for i = 0 to 8
    'nine passes 0-8
    debug.print i
next




                  

     


for {set i 0} {$i < 9} {incr i} {
    # nine passes 0-8
    puts $i
}

# alternate form
for {set i 0} {$i <= 8} {incr i} {
    # again, nine passes 0-8
    puts $i
}

# another alternate form
for {set i 1} {$i <= 9} {incr i} {
    # nine passes 1-9
    puts $i
}

# yet another alternate form - less readable
set i 1
for {} {[incr i] <= 9} {} {
    # nine passes 1-9
    puts $i
}               

带有整数计数器的“for”循环。 在 Tcl(或任何其他语言)中,这等效于“while”循环。 在某些语言(如 VB)中,“for”不如“while”灵活。 在 Tcl 中,情况并非如此。 任何东西都可以用作初始化代码、测试-继续表达式和增量代码。 这些部分不限于执行任何特定操作,如您在最终示例中看到的那样。

dim c as new collection
dim o as object
c.add "Mark"
c.add "Roy"
c.add "Brian"
for each o in c
    debug.print o
next
                       



set c [list Mark Roy Brian]
foreach o $c {
    puts $o
}
                  
     

循环访问数据结构中的项。 在 Tcl 中,使用列表数据结构。 VB 没有直接等效项,但集合对象是最相似的。 请注意,由于使用方法调用对象的开销,VB 集合在典型操作中比 Tcl 列表慢得多。 另请注意,foreach 命令的更强大和更具创造性的用法 未在此处显示。 这些在 VB 中没有直接等效项。

dim s as string
select case s
    case "John"
        debug.print "Mellencamp"
    case "Steve"
        debug.print "Tyler"
    case else
        debug.print "Unknown"
end select
                       


switch -exact $s {
    John {puts Mellencamp}
    Steve {puts Tyler}
    default {puts Unknown}
}
                

       

多选一执行。 请注意,Tcl 版本区分大小写。 在 VB 中,通常不区分大小写,具体取决于模块生效的“option compare”。-exact选项指定需要完全字符串匹配,而不是模式匹配或正则表达式匹配(这与区分大小写无关)。 另请注意,switch 命令的更强大和更具创造性的用法未在此处显示。


on error goto handler
debug.print a 'a is undeclared.
...
handler:
debug.print err.number, err.description




                       

if [catch {
    puts $a ;# a has not been set
} my_err] {
    puts "error message: $my_err"
    puts "stack trace: $errorInfo"
    # these things would have been shown
    # by the default error handler anyway.
} else {
    puts {All is well.}
    # the else block is optional.
}
                       

错误处理。 在 VB 中,简洁地处理错误可能是一个问题,尤其是当需要根据代码的哪个部分失败采取不同的操作时。 Tcl catch 命令巧妙地解决了这些问题。 此外,Tcl 自动提供失败代码的堆栈跟踪。 在 VB 中,如果应用程序在生产中(不在 IDE 中)需要堆栈跟踪,则必须由代码显式构建堆栈跟踪。 这对于 Tcl 在现场调试时是一个优势。 请注意,catch 返回一个布尔值 1 或 0,通常与“if”一起使用,如此处所示。

(No equivalent)
                       

set i [expr $e]
                       

将任意数学表达式传递给解释器进行评估。 这可能是用户输入的表达式,也可能是由较早的代码组成的。 这是 Tcl 最强大的方面之一。 它在 VB 中根本不可用。

(No equivalent)
               

set s [eval $c]
                       

将任意代码传递给解释器以执行。 这可能是用户输入的一些脚本,也可能是由较早的代码组成的。 这是 Tcl 最强大的方面之一。 它在 VB 中根本不可用。

(No equivalent)
               

source my_script.tcl
                       

将任意文件名传递给解释器以执行该文件作为脚本。 这是 Tcl 最强大的方面之一。 它在 VB 中根本不可用。

(No equivalent)

                       

set var_name marks_age
incr $var_name
                       

对任意选择的变量执行操作。 此处显示的代码将递增变量marks_age。 它的名称(字符串“marks_age”)存储在变量var_name中。 事实上,每个命令的所有部分都会在执行之前经过解释器的一次替换。 因此,任何命令的任何部分(甚至命令本身的名称)都可以根据数据或任何其他标准进行更改。 这是 Tcl 最强大的方面之一。 它在 VB 中根本不可用。

dim s as string
dim li as string
dim f_num as integer
s = ""
f_num = freefile
open "my_file.txt" for input as #f_num
while not eof(f_num)
    line input #f_num, li
    s = s & li & vbCrLf
wend
close #f_num
                       




set f [open my_file.txt r]
set s [read $f]
close $f




                       

将整个文件读入变量。 对于中等大小的文件,此 VB 代码非常慢。 并且它无法处理数据中的换行符。 Tcl 代码接受并保留数据中的换行符。 它还将不同的换行符标准化为一种标准化的换行符(默认情况下)。 此代码同样适用于原始数据、Tcl 列表或 Tcl 数组。ropen 命令中表示“读取”模式。

dim a(1 to 3) as string
a(1) = "Mark"
a(2) = "Brian"
a(3) = "Roy"
'oops - need more elements
redim preserve a(1 to 10) as string
a(4) = "John"
                       

array set a [list 1 Mark 2 Brian 3 Roy]
set a(4) John
# now some different kinds of
# element names in the same array
set a(Red) Hat
set a(Linux,RedHat) 7.1

                       

数组与数组。 VB 数组仅限于使用数字作为下标(下标或索引在 Tcl 中称为“元素名称”)。 并且必须声明数组为特定大小 - 扩展它需要一个(慢)“ReDim Preserve”操作。 Tcl 数组会自动扩展,并且它们使用超高效的哈希表实现来处理甚至数十万个元素,速度非常快。 Tcl 将任何类型的数据用于元素名称,甚至可以在同一个数组中混合不同的样式。 对每个元素中的维度数量没有限制。 Tcl 提供了简单的方法来迭代数组,或者仅迭代数组中的某些元素(按过滤器)。 您还可以获取元素名称的完整或部分列表,并比在 VB 中更方便地执行其他操作。 要在 VB 中获得这些功能的一部分,需要使用集合或字典对象。 每个对象都有其自身的怪癖和陷阱,例如比 VB 数组更高的开销。

(No equivalent)

                       

array set my_array $my_list
set my_list [array get my_array]
                       

列表到数组,再返回。 在这两种主要数据结构之间轻松快速的转换意味着每种结构的工具都可以应用于两者。 它们相互增加对方的效用。

dim a(1 to 100) as string
dim i as integer
dim f_num as integer
f_num = freefile
open "my_file.txt" for output as #f_num
for i=1 to 100
    print #f_num, a(i)
next
close #f_num                       


set f [open my_file.txt w]
puts $f [array get a]
close $f


                       

写入整个数组。 在此 VB 代码中,以及经常在其他 VB 代码中,数据中出现的新行和其他字符将在后续步骤(读取)中导致错误。 每当您的代码处理用户输入的任意数据时,这就会成为一个问题。 在 Tcl 中,它们不会 - 数据始终保持“干净”。 此外,回车符(0x0D 或十进制 13)和换行符(0x0A 或十进制 10)的各种组合默认情况下会自动标准化。 请注意,这两个示例不会生成相同的输出文件。 与 VB 一样,Tcl 示例写入一个纯文本文件。 但是 Tcl 文件将被读回(由 Tcl),并且自动具有相同数量的元素、相同的元素名称等等。 Tcl 列表数据结构用于此。 使用它可确保数据以简洁、明确的文本表示形式格式化。 它也是人类可读写的。

(No equivalent)

                       

set f [open my_file.txt w]
puts $f [array get a red*]
close $f
                       

写入数组的某些元素。 在 VB 中,必须为此使用集合或字典对象。 循环将迭代所有元素并根据需要选择它们。 在 Tcl 中,数组的名称是a,并且字符串模式red*(区分大小写)用作高速选择元素的过滤器。

(No equivalent)
                       

set my_list [lsort $my_list]
                       

对列表进行排序。 排序可以反转,或按数值排序等。它还可以使用索引元素对子列表列表进行排序。 Tcl 包含一套完整的命令,用于操作列表数据结构。 另请参见 lappendlinsertlreplacelsearchconcatsplitjoin 等。Tcl 列表也可以任意嵌套,并且 foreach 命令可以轻松处理它。

' requires a reference to ADO
' assume we have a connection called conn
dim rs as new recordset
rs.open "select id, name, age from people", _
    my_connection, adOpenStatic
' processing code goes here
rs.close
set rs=nothing                      

package require tclodbc
# assume we have a connection called conn
conn read a "select id, name, age from people"
# processing code goes here
unset a ;# get rid of this array
                       

从数据库表中检索简单的数据数组。 在 VB 中,数据始终在记录集对象中检索。 在 Tcl 中,可以根据您的需要以及正在使用的数据库包将其读取到数组和/或列表中。

(No equivalent)


                       

package require http
set httpTrans [http::geturl $pageURL]
upvar #0 $httpTrans state
if {$state(status) == {ok}} {
        puts $state(body)
}                       

从 Web 服务器检索文档或文件。

(No equivalent)
                       

regexp -all {src=['"](.+?)['"]} $body my_images
                       

复杂的字符串模式搜索和提取。Tcl 使用正则表达式来实现这一点。正则表达式是一种用于匹配字符串模式的规范,概念上类似于 VB 中 'like' 运算符使用的通配符模式,但功能更强大,简直是打了强力激素。正则表达式比 'like' 模式强大和灵活得多。有关正则表达式的非正式介绍,请参阅 http://zez.org/article/articleprint/11。Tcl 的正则表达式解析器是用手工优化的 C 代码编写的,可以通过几个不同的命令(regexpregsublsearch 等)在 Tcl 中使用。您习惯的更简单、功能较弱的版本也可以在几个不同的命令中使用(globstring matchlsearch 等)。这个例子需要 15 到 50 行 VB 代码,具体取决于它的健壮性以及对不同情况的容忍程度。此外,这些代码是 VB 中最困难、最容易出错和最慢的代码之一(经验之谈)。在这里,代码可以快速获取 HTML 页面上每个图像的 URL 列表。

(No equivalent)

                  

set find {<tr>(.*?)<td>(.*?)</td><td>(.*?)</td><td>(.*?)</td>(.*?)</tr>}
set replace {<tr>\1<td width=20%>\2</td><td width=40%>\3</td><td width=30%>\4</td>\5</tr>}
regsub -all -nocase $exp $body $replace result
puts $result                      

复杂的字符串模式搜索和替换。同样,Tcl 使用正则表达式。这个例子需要 40 行或更多的 VB 代码,特别是如果它在逻辑上组织得足够好,并带有足够的注释供维护程序员理解。而且,同样,它是 VB 中最困难、最容易出错和最慢的代码之一。在这里,HTML 正文中每一行的三个单元格的集合被系统地更改,同时保留每个单元格的内容。

(No equivalent)

                       

set handle [socket markhpc.dcisite.com 2000]
set greeting [read $handle]
close $handle
                       

建立到网络套接字的连接(充当客户端)并检索数据。该示例假定服务器正在侦听指定主机的 TCP 端口 2000。


(No equivalent)
                

       

proc greeting {handle client_ip client_port} {
        puts $handle {Welcome to our greeting server!}
        close $handle
}
socket -server greeting 2000
                       

实现一个网络服务器来响应上面显示的客户端。 这是完整的脚本。 如果您使用的是 Wish(Tcl 窗口外壳),这将像显示的那样全天运行。 如果您使用的是 Tclsh(控制台 Tcl 外壳),请在末尾添加一个 vwait 命令,以使程序等待事件而不是在脚本末尾终止。 这两个外壳之间的差异是必要的且有意的,因为 Wish 默认情况下是事件驱动的,而 Tclsh 不是。