体验Tcl语言


Tcl脚本初体验

Tcl(发音 tickle)是一种脚本语言。学习它的目的在于有本书上有这个语言的代码。但是过于老旧,无法运行了,想要自己修改下。:)

Tcl 的特性包括:

  • 任何东西都是一条命令,包括语法结构(for、if 等),以波兰表示法书写。

  • 命令通常可变。

  • 任何事物都可以重新定义和重载。

  • 所有的数据类型都可以看作字符串,包括源代码。

  • 拥有完全动态、基于类的对象系统 TclOO,支持包括元类、过滤器和mixin在内的高级功/能。

  • 提供事件驱动给套接字和文件。基于时间或者用户定义的事件也可以。

  • 默认的变量作用域是词法作用域,但 uplevel 和 upvar 允许过程与封闭的函数作用域/交互。

  • 所有的内置命令会在误用时产生错误消息。

  • 很容易用 C、C++ 或者 Java 扩展。

  • 解释语言,支持字节码。

  • 完全的 Unicode (3.1)支持,1999 年首次发布。

  • 跨平台。支持 Win32、UNIX、Linux、Mac 等。

  • 代码紧凑,易于维护。
  • 其实Tcl最大的特性在于它是一种字符串语言,几乎东西都默认为字符串。

    Wiki示例

    维基百科给出了两个示例,可以体验一下Tcl的强大:

    echo_server.tcl:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #!/bin/sh
    #Tcl ingores the next line but 'sh' doesn't \
    exec tclsh $0 ${1+"$@"}
    # echo服务器可以处理并发请求
    proc newConnection { sock addr port } {
    # 打印出来连接信息
    puts get_one_client
    puts $addr
    puts $port
    # socket以行模式 无阻塞 接收数据
    fconfigure $sock -blocking no -buffering line
    # 当socket可读时 调用handleData函数
    fileevent $sock readable [ list handleData $sock ]
    }
    proc handleData { sock } {
    # 保存client socket传来的数据到info变量,并打印出来info
    set info [ gets $sock ]
    puts $info
    # 将info写入client socket
    puts $sock $info
    # 到EOF 关闭socket
    if { [ eof $sock ] } {
    close $sock
    }
    }
    # 设置端口
    set port [ lindex $argv 0 ]
    socket -server newConnection $port
    # 一直运行
    vwait forever

    语法还是很简练的,可以使用以下的Python代码测试连接服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #coding=utf-8
    import threading
    import socket
    import time
    class MyThread(threading.Thread):
    def run(self):
    msg = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Expedita repudiandae, \
    laborum non officiis. Ratione debitis sapiente nesciunt, quas dolore, magni aspernatur \
    ipsum numquam error quae eligendi aliquid adipisci omnis in.\n"
    try:
    sock = socket.socket()
    sock.connect(('localhost', 12345))
    sock.send(msg)
    res = sock.recv(2048)
    print res
    sock.close()
    except Exception as error:
    print error
    sock.close()
    time.sleep(10)
    def main():
    print "Start main threading"
    threads = [MyThread() for i in range(300000)]
    for t in threads:
    t.start()
    for t in threads:
    t.join()
    print "End Main threading"
    if __name__ == '__main__':
    main()

    运行时需要将server运行于12345端口。然后运行python echo_client.py就可以了。
    可以看出简单几行Tcl代码,实现的功能还不少(可以异步处理TCP请求)。

    三行实现一个简单的时间显示GUI程序:

    1
    2
    3
    4
    5
    6
    #!/usr/bin/wish
    proc every {ms body} {eval $body; after $ms [info level 0]}
    pack [label .clock -textvar time]
    every 1000 {set ::time [clock format [clock sec] -format %H:%M:%S]}

    虽然以上代码还不能完全理解,但是也能猜个大概,以后慢慢熟悉。
    这里tclsh和wish都是Tcl语言的交互解释器,wish提供了一个GUI环境,可以通过简单代码构建带有图形界面的窗口应用。

    下面通过几个示例脚本初步了解下Tcl的语法:

    hello.tcl:

    1
    2
    3
    4
    5
    6
    7
    8
    #!/usr/bin/tclsh
    expr 2*10 - 1; # expr计算表达式的值
    expr 2**10 + 1;
    puts bye; # 输出bye
    puts "\a"; # 警告音
    exit 1; # exit 1退出

    一个计算阶乘的实例:

    factorial.tcl:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #!/bin/sh
    #Tcl ingores the next line but 'sh' doesn't \
    exec tclsh "$0" "$@"
    proc factorial {val} {
    set result 1
    while {$val > 0} {
    set result [expr $result*$val]
    incr val -1 # incr自增变量 -1为增量
    }
    return $result
    }
    puts [factorial 10] # 使用方括号调用函数
    puts [factorial 1]
    puts [factorial 20]
    puts [factorial 50]

    最简单GUI应用(一个按钮,点击退出)

    1
    2
    3
    4
    5
    #!/usr/bin/wish
    button .b -text "hello world!" -command "puts bye\a; exit"
    pack .b

    这里.b类似HTML中的id,-text参数类似HTML中的button元素上的内容 -command 参数是点击按钮时候的效果,这里打印出bye和警告音,然后退出。

    一个计算阶乘的GUI应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #!/bin/sh
    #Tcl ingores the next line but 'sh' doesn't \
    exec wish "$0" "$@"
    proc factorial {val} {
    set result 1
    while {$val>0} {
    set result [expr $result*$val]
    incr val -1
    }
    return $result
    }
    entry .value -width 6 -relief sunken -textvariable value
    label .description -text "factorial is "
    label .result -textvariable result
    # 按下时候 将result的值设置为[factorial $value]返回值
    button .calculate -text "Calculate" -command {set result [factorial $value]}
    button .exit_btn -text "Exit" -command "puts exit; exit"
    # 给.value元素绑定回车事件
    bind .value <Return> {
    .calculate flash
    .calculate invoke
    }
    # 将各个元素加入画板,并设置距离(类似CSS的padding)
    grid .value .description .result -padx 1m -pady 1m
    grid .calculate - - -padx 1m -pady 1m
    grid .exit_btn - - -padx 1m -pady 2m

    打印出来鼠标在画板的位置,如果按下键盘a字母输出信息”a is pushed”

    position.tcl:

    1
    2
    3
    4
    5
    6
    7
    #!/bin/sh
    #Tcl ingores the next line but 'sh' doesn't \
    exec wish "$0" "$@"
    bind . <Motion> {puts "pointer at %x,%y"}
    bind . <a> {puts "a is pushed"}

    以下是一些小的代码片段组成的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    #!/bin/sh
    #Tcl ingores the next line but 'sh' doesn't \
    exec tclsh "$0" "$@"
    # 输出表达式的值
    puts [expr sin(10)]
    # 定义变量num
    set num 11111
    puts "num is $num"
    puts [expr $num % 8]
    # index from 0
    puts [lindex {red blue pink} 2]
    set length [string length aaaaaaabbbbbb]
    puts "length is $length"
    # 变量替换
    set a 1
    set b 2
    puts "$a+$b = [expr $a+$b]"
    # 字符串中使用双引号
    set name a.out
    set msg "Counldn't open file \"$name\""
    puts $msg
    # 使用{}不转义$符号
    set msg {Eggs: $2.18/dozen Apple: $3.09/dozen}
    puts $msg
    proc counter {value list} {
    set count 0
    foreach el $list {
    if $el==$value {
    incr count
    }
    }
    return $count
    }
    # 调用counter函数
    puts [counter 1 {1 2 3 4 5 6 1 1 }]
    # glob函数
    puts [glob *.tcl]
    # format 函数
    set name color_blind
    set greet [format {"%s" said: hello world!} $name]
    puts $greet
    set city "Los Angeles"
    set bigcity $city
    puts $city

    运行结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -0.5440211108893699
    num is 11111
    7
    pink
    length is 13
    1+2 = 3
    Counldn't open file "a.out"
    Eggs: $2.18/dozen Apple: $3.09/dozen
    3
    clock.tcl echo_server.tcl factorial.tcl get_pos.tcl gui_factorial.tcl hello.tcl style_button.tcl try.tcl var_example.tcl
    "color_blind" said: hello world!
    Los Angeles