加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

【网络】一小时了解tcl语言

(2011-11-08 09:29:53)
标签:

多态性

变量

命令

数组

构造函数

分类: iFPGA


     TCL 语言非常简单,下面我们通过一个简单的例子,让我们对TCL 有一个大概的印象和了解。如果你会使用C Shell 脚本或者有过C/C++的经验,那么TCL 对你而言,应该非常容易。先看看下面的程序complex.TCL:

          #File complex.TCL
          #
          package require Itcl
          package require control

          itcl::class Complex {
               public variable m_r          ;#实数
               public variable m_i          ;#虚数

               constructor {r i} {
                    set m_r $r
                    set m_i $i
               }

               public method + { c } {      ;#复数的加法
                    set r [expr "$m_r + [$c cget -m_r]"]
                    set i [expr "$m_i + [$c cget -m_i]"]
                    return [code [Complex #auto $r $i]]
               }

               public method GetReal { } {  ;#复数的减法
                    return $m_r
               }

               public method GetImag { } {
                    return $m_i
               }

               public method - { c }
          }

 itcl::body Complex::- {c} {
              set r [expr "$m_r - [$c cget -m_r]"]
              set i [expr "$m_i - [$c cget -m_i]"]
             return [itcl::code [Complex #auto $r $i]]
         }

         pro main {} {     ;#定义了过程main
              set r 100;set i 200
              Complex a $r $i
              Complex b 50 50
              set c [a - b]

              control::control assert enabled 1
             puts “c.real = [$c GetReal] ; c.imag = [$c GetImag]”
              control::assert "[$c GetReal]==50"
              control::assert "[$c GetImag]==150"
         }

         main          ;#调用过程main

     上面的代码实现了一个简单的复数类,并且实现了几个成员函数,完成加法和减法操作。最后的代码创建两个对象,并且相减得到另外一个对象。然后使用assert 来断定我们的操作是正确的。

启动解释器

     我们可以在Dos 提示符中输入命令tclsh 来启动TCL 解释器,并且进入交互式模式,然后使用source 命令来执行我们刚才创建的脚本,如下:

         C:\>tclsh
         % cd e:/work/script
         % source complex.TCL
         c.real = 50 ; c.imag = 150
         %

     交互式模式下,我们每输入一次命令,TCL 解释器就执行这命令,然后把命令的执行结果给打印出来。如果出现了语法错误或者异常,就把异常信息给打印出来。
     我们也可以在DOS 提示符下输入tclsh e:/work/script 直接执行我们的脚本,如下:
 C:\>tclsh e:/work/script/complex.tcl
        c.real = 50 ; c.imag = 150

        C:\>

    ActiveTCL 软件包中还有一个程序tkCon,可以在启动菜单中找到。这是一个使用TCL/Tk 开发的图形界面的脚本解释器,使用起来更加方便。

变量和表达式

    TCL 中也存在变量的概念,我们可以创建变量,赋值,引用,删除变量,比如上面的代码中:

        % set r 100;set i 200  ;#创建了两个变量r 和i,并且初始化为100 和200
        200
        % Complex m $r $i      ;#引用变量r 和i,创建了一个Complex 对象
        m
        %

    TCL 中的变量没有类型,或者我们换一种说法,所有的变量都是同一种类型:“字符串”类型。比如上面例子种的变量r 和i,他们的值分别是100 和200,这里100 和200 都是字符串,没有整数这么一说。可能我们会迷惑了,如果我要计算这两个数的和,怎么办?

        % set sum $r+$i        ;#企图计算r 和i 两个变量的和,但是事与愿违;
        100+200
        % set sum [expr $r+$i] ;#只有这样才行
        300
        %

    看看上面的例子就明白了,第一种方法计算sum,结果sum 的值是字符串“100+200”。只有通过第二种方式,使用expr 命令来计算表达式$r+$i,sum 的值才是我们期望的300。在TCL  中,所有的数学计算都是通过expr 命令来实现的。例如通过计算正弦来得到2  的平方根:

        % set PI 3.1415926535897932    ;#创建变量PI
        3.1415926535897932
        % expr "cos($PI/4)*2"          ;#计算得到sqrt(2)
        1.41421356237
        % expr "sqrt(2)"               ;#直接计算得到 sqrt(2)
        1.41421356237

 TCL 中表达式的写法和C 语言是比较类似的,并且支持常用的数学函数。

定义函数

    Pascal、VB 语言中存在函数和过程的区别,并且函数和过程的定义方式不一样。在TCL语言中,函数和过程的定义方式没有差别,就像C/C++一样,所以后面的章节中针对TCL而言,“函数”和“过程”两种提法是等价的。TCL 中的过程分成两类:
    1.  TCL 语言自带的核心命令。
    2.  用户编写的扩展命令。
    TCL 的核心命令只有80 多条,比如set 命令;我们还可以使用TCL 脚本定义自己的过程,也可以使用C/C++语言来实现一些和操作系统等紧密相关的过程。前面的例子代码中,我们就定义了一个过程main;下面是另外一个过程定义:

        #过程Factorial,计算参数n 的阶乘。
        proc Factorial {n} {
            if {$n<=1} {
               return 1
            }
            return [expr $n*[Factorial [expr $n-1]]]
        }

        puts "10! = [Factorial 10]" ;#调用过程Factorial,计算10!
        puts "5! = [Factorial 5]"  ;#调用过程Factorial,计算5 !

    可见,TCL 中的过程是可以递归调用的。

循环和控制

    TCL  的核心命令中提供了常见的控制结构。而且TCL 和其他语言相比很大的不同是:你甚至可以编写你自己的控制结构!这是后话。TCL 中循环结构包括for 循环和while 循环,
例如:

        #使用for 循环实现阶乘
        proc Factorial1 {n} {
            set result 1
            for {set i 1} {$i<=$n} {incr i} {
                set result [expr $result*$i]
            }

       return $result
          }

          #使用while 循环来实现阶乘
          proc Factorial2 {n} {
               set result 1
               set i 1
               while {$i<=$n} {
                   set result [expr $result*$i]
                   incr i
               }
               return $result
          }

          puts "10! = [Factorial1 10]"
          puts "5! = [Factorial2 5]"

     还有一个很有用的foreach 循环,功能和VB  中的foreach 类似,但是要强大和灵活的多。后面我们详细讨论;
     可以使用if 来进行判断分支结构,使用switch  实现多路匹配选择。例如前面采用递归方式定义的Factorial 函数中,就使用了if 结构。switch 可以指定字符串匹配模式来进行匹配选择,例如:

          set c "http://www.microsoft.com"

          #根据正则表达式匹配方式来进行switch 选择
          switch -regexp $c {
               "http://.+"   {puts "$c is a http url"}
               .+@.+         {puts "$c is a email address"}
               ftp://.+      {puts "$c is a ftp url"}
               default       {puts "Other ..."}
          }

     和C/C++类似,TCL 中也有break 和continue 语句。主要用来在循环中控制循环:break跳出最里层的循环,continue 掠过本次循环中下面没有执行到的语句,继续下一次循环。要注意的是,switch 中各个子句中没有必要象C/C++一样,加上一个break。

列表和数组

    TCL 中变量分成简单变量和组合变量。组合变量分成两种:列表和数组。这两种组合变量的数据结构虽然简单,但是功能强大。很多人抱怨,TCL 中怎么不能够象C/C++那样定义struct 或者union 这样的结构?这是拿C 语言的思维来使用高级脚本,是行不通的。实际上,有了列表和数组,基本上很少有实际问题无法解决。
    列表是一个多个元素的有序的集合,每一个元素通过下标来进行操作,下标从0 开始,列表中的元素可以是另外一个列表,例如:

        set students {
             {LeiYuhou   Mail         27}
             {Lily       Femail       25}
             {Tiger      Mail         2}
         }

        set index 1
        foreach s $students {
             foreach {name sex age} $s {
                 puts "$index -> $name"   ;#打印出序号和名字
             }
             incr index
         }
        puts [lindex $students end]

    上面的代码中,students 就是一个列表,里面三个元素同时也还是列表。通过foreach 循环把名字给打印出来。最后的语句通过lindex 命令取出列表的最后一个元素,并且打印出来。
    TCL  中的数组则是多个元素的无序的集合,每一个元素都包含两个值:下标(key)和值(value )。在一个数组中,所有元素的下标都是互不相同的,元素通过下标来进行索引和操作。元素值可以是字符串,也可以是列表。例如:

        array set DAY {
             0 Sunday
             6 Saturday
             5 Friday
             4 Thirsday
             3 Wednesday
             2 Tuesday
                           ;#初始化数组变量DAY
        set Day(1) Monday     ;#设置DAY 数组的下标为1 的元素


        puts "5 - $DAY(5)"  ;#输出第五天的名字
        puts "keys - [array names DAY]" ;#输出所有的下标

    可以看到,TCL 中的数组和我们C 语言中熟悉的数组完全不是一回事,倒是和VBScript中的对象Dictionary 非常的类似。TCL 中没有多维数组的概念,但是后面我们会讲到如何使用多维数组。

输入输出

    TCL  核心中提供了文件输入输出的命令,其中标准输入和标准输出可以看成特殊的文
件:它们在进程启动的时候自动打开。TCL 中的输入命令是gets,输出命令是puts:

        set v 1
        set fh [open "C:/a.txt" w]     ;#打开C:\a.txt 文件
        fconfigure $fh -translation crlf ;#配置成文本模式

        while { $v!="" } {
            puts -nonewline "Please input you name:" ;#输出提示信息
            gets stdin v                       ;#从标准输入读入一个字符串
            puts $fh "your name $v"            ;#写入到文件中去
        }

        close $fh       ;#关闭文件句柄;

    gets 和puts  不仅可以操作磁盘文件,还可以操作串口,操作socket 句柄等。TCL 来源于UNIX,众所周知,UNIX  里面的文件是不区分文本和二进制的,但是为了兼容多种操作系统,这里增加了一个命令fconfigure 用来配置文件句柄属性,包括模式,缓冲区大小等。

类和面向对象

    这里我们不介绍面向对象编程的概念,如果你还不知道什么是面向对象,可以找一本C/C++中相关部分的介绍先了解一下面向对象的基本概念。
    TCL 是基于命令的语言,一个TCL 程序是由多个命令的线性组合,本来它是不支持面向对象编程的。但是TCL  的一个重要特点就是具有非常良好的扩展性,所以网络上出现了不少面向对象的扩展包,其中最有名的就是ITCL,我们前面的例子中复数类就是采用ITCL扩展包写成的一个类。ITCL  使TCL具备了完备的面向对象特性,并且在形式和语法上和C/C++非常类似:

 1.  封装:每一个成员变量或者函数,都可以指定三种保护方式的一种:public、protected 

           和private;其意义和C++中完全一致;
    2.   继承:一个类可以从多个类中派生,也就是说,一个类可以有几个基类;
    3.   多态:ITCL 中类的任何一个成员函数都是虚函数!所以显然支持多态性了。
     除了这三点,ITCL 类还支持构造函数和析构函数,其功能和C++的类似。
    看看下面一个多态性的例子:

         #使用ITCL 必须引入ITCL 扩展包
         package require Itcl
         namespace import itcl::*

         #定义了基类CPerson
         class CPerson {
             protected variable m_name      ;#成员变量,保护类型,可以被继承
             protected variable m_sex

             constructor {name sex} {       ;#构造函数
                  set m_name $name
                  set m_sex $sex
             }

             public method PrintInfo {} {   ;#public 方法,输出对象信息,可以被继承
                 puts "CPerson [GetInfo]"   ;#调用了成员函数GetInfo
             }

             public method GetInfo {} {
                 return "name=$m_name; sex=$m_sex"       ;#返回对象信息
             }
         }

         class CStudent {
             inherit CPerson            ;#表示本类从CPerson 继承

             protected variable m_age

             constructor {name sex age} {   ;#构造函数
                  CPerson::constructor $name $sex} {     ;#调用基类的构造函数
                  set m_age $age


            }

            public method GetInfo {} { ;#返回对象信息
               return "name=$m_name; sex=$m_sex; age=$m_age"
            }
        }

        CPerson a "LeiYuhou" M     ;#构造CPerson 对象实例
        CStudent b "Lily" F 20     ;#构造CStudent 对象实例

        a PrintInfo    ;#分别输出两个对象的信息
        b PrintInfo

    上面的代码中,声明了两个对象,CPerson 是CStudent 类的基类,CPerson 基类中声明了一个函数PrintInfo 用来输出一些信息,这个函数在CStudent  中也可以被调用。这个函数在基类CPerson  中定义,调用了另外的一个函数GetInfo,这个函数在基类和继承类中都有各自不同的定义;PrintInfo 究竟应该调用哪一个GetInfo,就看调用这个函数的对象类型。
    上面的代码中,a 是CPerson 类型,b 是CStudent 类型,所以上面的代码执行结果是:

        CPerson name=LeiYuhou; sex=M

        CPerson name=Lily; sex=F; age=20

    C++对象的多态性必须通过对象指针或者引用来体现,也就是说,只有通过对象指针来调用虚函数,C++的多态性才能够体现出来。这样的原因在于C++是通过虚表(virtual table 的方式来实现多态性的。ITCL中没有指针这么一说,那么多态性如何实现的呢?这个问题我没有深入研究,但是一般脚本语言的多态性都是通过查表回溯的方式来实现,比如Python。TCL 应该也是采用类似的方法实现面向对象的多态性的。

函数库程序包

    一个大型程序往往不是由一个文件组成的,一般需要很多其他的函数库。C/C++语言中,先逐个编译程序单元,再连接(link)所有的obj  文件以及使用到的库文件,最后生成目标程序。TCL 有两种自己特有的处理方式:函数库和程序包。
    函数库是比较古老的方式,和C 语言的函数库比较类似。TCL 解释器在执行命令的时候,如果碰到不认识的命令,就会通过一定的索引方式在特定的位置寻找它。这种方式不支持版本升级,现在使用的比较少了,一般情况下我们使用另外一种方式:程序包。
    程序包可以使用TCL 语言编写,也可以使用C/C++来实现;比如我们刚才提高的面向对象,就是通过扩展包ITcl 来实现的。一个脚本在使用某一个扩展包之前,必须先将扩展包引入,语法如下:

        package require ?-exact? package ?version?

    例如,为了使用http 协议处理包:

        package require –exact http 2.4.5

    其中包名字参数是必须的。如果指定选项-exact,表示加载指定的版本。在网络上存在很多很多Tcl 的扩展包,并且大部分是免费的,安装也非常方便:只要把相关的文件复制到特定的目录中即可。所以建议采用package 的形式来开发TCL 扩展代码。

总结

    这一章我们介绍了TCL 的基本语法和简单的用法,给大家一个对TCL 粗略的认识。但是要真正掌握TCL  的使用,还有很多细节性的东西需要进一步了解。后面的章节中我们针对这一章出现的各个概念进行详细的解释。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有