load help; @[name] dao.guide @[name] @[title] 快速简明指南 @[title] @[text] 本指南将简明快速地介绍道语言编程的一些基本方面。 @[section] 基础 @[section] 我们将从经典的@[green]世界你好@[green]例子开始。 @[subsection] 世界你好 @[subsection] 在屏幕简单地打印“世界你好”,可使用, @[code] io.write( "Hello world!" ) @[code] 这个例子使用了内置的模块@[green]io@[green]来访问输出输入(IO)功能。 @[green]write()@[green]是@[green]io@[green]模块提供的一个方法, 可用来在标准输出打印。在道里,字符串通常由单引号或双引号来引用表示。 道语言命令语句后面通常可加也可不加分号。 @[subsection] 运行程序 @[subsection] 道支持三种方式运行。 最通常的方式是将代码写入一个文件(hello_world.dao), 然后在命令行使用道解释器执行: @[code] $$ dao hello_world.dao @[code] 另一种方式是将代码直接从命令行传给道解释器: @[code] $$ dao -e "io.write( 'Hello world!' )" @[code] 最方便学习的执行方式是交互式执行。 从命令行,不加任何参数执行道解释器,将进入交互模式。 @[code] $$ dao Dao Virtual Machine 2.0 Built date: Jun 12 2014 Changeset ID: FOS.4d5eb15f0e53 Copyright(C) 2006-2014, Fu Limin Dao is released under the terms of the Simplified BSD License Dao Language website: http://daoscript.org (dao) @[code] 如果你道解释器安装正常,使用交互模式运行道将自动 载入帮助文件。并显示如下信息(或者此信息的英文版): @[code] 帮助模块已载入。 现在您可以使用"help()"来列出所有可用的帮助条目; 或者运行"help('help')"来获得此帮助系统的详细信息。 @[code] 如果你查看的是此帮助的网页版,你可以使用一下命令在交互模式下查此帮助: @[code(dao)] (dao) help( "dao.guide" ) @[code(dao)] 这些在线帮助有两个语言版本:中文和英文。 选择语言请使用: @[code(dao)] (dao) help::set_language("zh") # 选择中文; (dao) help::set_language("en") # 选择英文; @[code(dao)] @[subsection] 注释代码 @[subsection] 给自己的代码加注释通常是个好习惯。 道语言支持单行和多行注释。 单行注释以@[code]#@[code]开始,直到行末; 多行注释以@[code]#{@[code]开始,@[code]#}@[code]结束。 例如: @[code] # 这个是单行注释例子: io.write( "Hello World!" #{ 代码中注释 #} ); #{ 这里是多行注释例子。 这里是多行注释例子。 #} @[code] @[subsection] 常量,变量和定变量 @[subsection] 道支持显式地声明常量,局域变量,静态变量,全局变量和定变量(Invariable)。 常量可由关键词@[code]const@[code]声明: @[code] const DEFAULT_INDEX = 123 const DEFAULT_NAME = 'abc' @[code] 常量只能由常量表达式来初始化。顶层作用域声明的常量将是全局常量。 关键词@[code]var@[code]可用来声明局域或全局变量。 同样顶层作用域声明的变量将是全局变量,其他的将是局域变量。 @[code] var current_index = 456 # 全局变量; var current_name = 'def' # 全局变量; if( current_index ) { var temp_index = current_index # 局域变量; } @[code] 在每个可以使用@[code]var@[code]的地方,@[code]invar@[code]也可被使用, 以声明局域,全局或成员定变量。 定变量是一种特殊的变量,它的值一旦被初始化就不能被修改了。 @[code] var varlist = { 123 } invar invlist = varlist invlist.append( 456 ) # 错误! @[code] 详情请参看@[node]dao.data.invar@[node]。 另一种变量是使用关键词@[code]static@[code]声明的静态变量。 非类成员的静态变量实际上是个仅在其声明的作用域(局域)可见的全局变量。 因此,函数里的静态变量是保存在函数之外,且每次函数运行时所访问的静态变量 都是同一个变量。静态变量必须用常量来初始化。 如果没有上述关键词来限定,以@[code]name=expression@[code]形式声明的新量 都将是局域变量。如果该变量已被声明,该命令语句会将该变量重新赋值。 要避免这样,关键词@[code]var@[code]必须被显式地使用。 @[code] var index = 789 for(var i = 1 : 3 ){ var index = 123 # 循环内局域变量; } @[code] 详情请参看@[node]dao.data@[node]。 @[red] 提示:在交互模式下,所有顶级作用域声明的变量都将变成全局变量。 @[red] @[subsection] 类型标注 @[subsection] 在以上例子中,那些常量,变量和定变量都没有被声明类型信息。 它们的类型将通过自动推导来获得。 很多情况下,给常量,变量和定变量标注类型信息将很有好处。 有适当类型标注的程序将更易读懂和理解。当然它也将程序的 错误检测更容易。 类型标注一般被放在变量名后面,并加冒号分开。 @[code] const vector: array = [ 12.5, 34.6 ] invar invlist: list = { 12.5, 34.6 } var varlist: list @[code] 这里如果没有类型标注,"vector"将成为类型为"array"的数值数组。 而且"varlist"的声明没有初始化信息,它的类型信息将没法被自动推导出。 在后面你将看到,类型标注也可用在函数原型和类成员数据上。 详情请参看@[node]dao.type@[node]。 @[section] 基本类型 @[section] 为了方便编程,道对一些通用的数据类型有比较好的支持。 这些数据类型包括布尔类型,数字类型,枚举符号,字符串,数值数组, 元组,列表和关联表等。 @[subsection] 布尔类型 @[subsection] 布尔类型(@[code]bool@[code])是一种简单的逻辑数据类型,可用来表示真与假值。 @[code] var B1 = true var B2 = false var B3 = 3 > 2 var B4 = B1 and B2 @[code] @[subsection] 数字类型 @[subsection] 道对以下数字类型有原生支持:@[code]int@[code],@[code]float@[code] 和@[code]complex@[code]。 整数,浮点数及复数的虚实部均按64位存储。 道里面,整数和浮点数的表示方式跟其他语言类似。 带@[code]C@[code]后缀的数字表示复数的纯虚部。 例子: @[code] var I1 = 123 # 10进制整数; var I2 = 0xabc # 16进制整数; var F1 = 456.7 # 双精度浮点数; var F2 = 123e4 # 双精度浮点数; var F3 = 123E4 # 双精度浮点数; var C1 = 123C # 双精度复数,实部0.0,虚部123; var C2 = 456.6C # 双精度复数,实部0.0,虚部456.6; @[code] 这些数字类型支持大部通常的运算符。例如以下算术运算符都被支持: @[code]+@[code] (加法,正号), @[code]-@[code] (减法,负号), @[code]*@[code] (乘法) 和@[code]/@[code] (除法)等。 @[code] var I1 = 123 + 456 var I2 = 789 % 123 var F1 = 123.5 ** 3 var F2 = 789.5 / 123 var C1 = 12.3 + 45.6C @[code] 详情请参看 @[node]dao.type.int@[node],@[node]dao.type.float@[node], 和@[node]dao.type.complex@[node]。 @[subsection] 枚举符号 @[subsection] 一个枚举类型@[code]enum@[code]定义了一组名称符号,其中每个符号表示一个特定的整数值。 这个类型对于表示一组状态或标识特别有用。 @[code] enum MyEnum { AA , # 0 BB = 3, # 3 CC # 4 } @[code] 这些符号只能通过相应的枚举类型来访问, 应为它们并没有出现在当前的命名空间。 @[code] var a = MyEnum::AA # Or: MyEnum.AA; @[code] 但是,道语言里支持一种称作符号特殊的枚举类型。这种符号是由 普通的标识符前加货币符号@[code]$@[code]构成。 它们通常是与枚举类型一起使用。 @[code] enum E1 { AA=1, BB=2 } enum E2 { AA=10, BB=20 } var e1: E1 = $AA var e2: E2 = $AA io.writeln( e1 ) # 输出: $AA(1) io.writeln( e2 ) # 输出: $AA(10) @[code] 尽管是同一个符号,"$AA"在分别赋给"e1"和"e2"时,被自动地做了不同的处理。 在赋给"e1"时,"$AA"以等同E1::AA处理,而在赋给"e1"时,"$AA"以等同E2::AA处理。 因此符号所表达的东西将取决于它被用作的枚举类型。 使用枚举类型的另一种方式是使用枚举类型的类型名: @[code] var e1: enum = $AA var e2: enum = $BB @[code] 枚举类型还被细分为不同的子类型以支持不同的操作。 详情请参看@[node]dao.type.enum@[node]。 @[subsection] 字符串 @[subsection] 道语言字符串由一组字节组成,可存储任何数据。 但在当作文本处理是,它一般会当UTF-8编码处理。 字符串常量可由单引号或双引号引用的一组字符来表示。 @[code] var S1 = 'Something' var S2 = "道语言" var S3 = 'It\'s green' var S4 = "\u9053\u8bed\u8a00" # the same as S2: "道语言"; @[code] 在字符串常量里,有些字符必须使用反斜杠以转义符的方式表示。 其他的如@[code]\u@[code]则将跟着的数字转义为特定的字符。 如果要使用字符串常量而不处理转义字符,此字符串常量必须用@[code]verbatim@[code] 字符串表示。这类字符串由一对相同的以@[code]@[]@[code]形式出现的组合字符标记 引用。此方括号之间可包括任意多的字母,数字,下划线,空白字符,点,冒号,减号 和等号,以保证这个组合字符标记不出现在字符穿内容之中。 @[code] # C++ codes as verbatim string: var cpp = @[cpp x] class AA { int index; }; struct BB{}; @[cpp x] @[code] 字符串的内容可通过下标访问或修改: @[code] var str = "ABCDEFGHIJK"; io.writeln( str[1] ) # 第二个字符; io.writeln( str[:4] ) # 前五个字符; io.writeln( str[6:] ) # 从第六个开始后面所有字符; io.writeln( str[3:8] ) # 从第三个到第八个字符; str[1] = "X"; # 修改单个字符: str = "AXCDEFGHIJK"; str[2:5] = "123456" # 修改子字符串: str = "AB123456GHIJK"; @[code] 字符串也可通过@[code]+@[code]或@[code]+=@[code]连接: @[code] var str = "ABCDEFGHIJK"; var str2 = str + "123" # str2 = ABCDEFGHIJK123 str += "123" # 从后面添加字符串: str = ABCDEFGHIJK123 str += 88 # 从后面添加字符: str = ABCDEFGHIJK123X @[code] 道字符串类型还支持一系列方法以方便处理字符串数据。 这些方法还包括了字符串模式匹配相关的方法。 详情请参看@[node]dao.type.string@[node]。 @[subsection] 数值数组 @[subsection] 道对多维数值数组有原生的支持。这种数组可使用方括号@[code][]@[code] 或者打括号前加@[code]array@[code]关键字即@[code]array{}@[code]构造。 这种构造可以要么枚举向量或矩阵里的所有的元素或定义一个 算术级数(使用初始值,增量值,级数长度)生成这些元素。 @[code] var vec1 = [1, 2, 3] # array 向量, 即 1x3 矩阵; var vec2 = [1.0; 2; 3] # array 3x1 矩阵, 即转置向量; var mat1 = [1.0, 2; 3, 4] # array 2x2 矩阵; var mat2 = [ [1, 2], [3, 4] ] # 2x2 矩阵 var mat3 = array{ 1, 2; 3, 4 } # 2x2 矩阵 @[code] 类似字符串,数值数组的元素可通过下标访问或修改: @[code] var mat = [ 1, 2, 3; 4, 5, 6; 7, 8, 9 ]; # 3x3 矩阵; var rowvec = mat[1,:] # 第二行; var colvec = mat[:,1] # 第二列; var submat1 = mat[:1,:] # 头两行; var submat2 = mat[:,1:] # 头两列; var submat3 = mat[:1,1:] # 头两行和尾两列交叉部分子矩阵; mat[0,:] = [11, 22, 33] # 将首行设置为 [11, 22, 33]; mat[:,1] += [11, 22, 33] # 将 [11, 22, 33] 加到第二例; mat[:,1] += 100 # 将 100 加到第二例; mat[:1,1:] += [10, 20; 30, 40] # 将 [10, 20; 30, 40] 加到下标表示的子矩阵; @[code] 详情请参看@[node]dao.type.array@[node]。 @[subsection] 列表 @[subsection] 类似数组, 列表也可以通过元素枚举或以算术级数生成的方式来创建。 只不过列表需要用@[code]{}@[code]或@[code]list{}@[code]创建。 @[code] var list1 = { 1, 2, 3 } # list var list2 = { 1.0, 2, 3 } # list var list3 = { 1 : 2 : 5 } # list var list4 = { 'abc', 'def' } # list var list5 = { 123, 'abc' } # list @[code] 列表元素也可通过下标访问或修改: @[code] var alist = { 0, 1, 2, 3, 4, 5 } var item = alist[1] var sublist = alist[2:4] alist[3] = 10 alist[4] += 10 @[code] @[subsection] 关联表(字典) @[subsection] 关联表(字典)是一种将键与值以成对的方式组织成适当的数据结构以便高效地进行键值查找。 可以将键以有序的方式历遍的是有序关联表,否则是无序关联表。无需的关联表一般是基于 哈希表,可以支持比有序表更高效的键查找。 道语言里,有序关联表的创建使用@[code]{key=>value...}@[code] 或@[code]map{key=>value...}@[code]; 而无序关联表则使用@[code]{key->value...}@[code]或 @[code]map{key->value...}@[code]。 @[code] var amap = { 'abc' => 123, 'def' => 456 } var ahash = { 'abc' -> 123, 'def' -> 456 } var amap = map{ 'abc' => 123, 'def' => 456 } var ahash = map{ 'abc' -> 123, 'def' -> 456 } @[code] 关联表的下标访问与列表类似,只不过这里下标是键: @[code] var amap = { 'abc' => 123, 'def' => 456, 'ghi' => 789 } var value = amap[ 'abc' ]; var submap = amap[ 'abc' : 'def' ]; @[code] @[subsection] 元组 @[subsection] 元组是一种储存固定数目元素的数据结构。并且元组里的每个元素支持单独的类型标注。 另外,还可给元组里每个元素指定一个成员名。 它的创建方式类似关联表,只不过要用@[code]()@[code]或@[code]tuple{}@[code]。 @[code] var tup1 = ( 123, 'abc' ) # 无成员名的元组; var tup2 = ( index = 123, 'abc' ) # 第一个元素的成员名为"index"; var tup3 = tuple{ 123, name = 'abc' } # 第二个元素的成员名为"name"; @[code] 元组里的每个元素都可通过下标访问或修改。有成员名的元素也可通过成员名访问。 对元组进行下标切片将产生新的元组。 @[code] var tup = ( index = 123, 'abc', [1,2,3] ) var id = tup[0] id = tup.index tup.index = 456 var tup2 = tup[:1] # ( index = 123, 'abc' ) @[code] @[section] 控制结构 @[section] 程序的逻辑主要是通过控制结构来表示。道语言支持以下常用的控制结构: @[green]if-else@[green], @[green]for@[green], @[green]while@[green], @[green]do-while@[green], @[green]switch-case@[green], @[green]break@[green] 和 @[green]skip@[green] 等。 @[subsection] If-Else条件控制 @[subsection] 条件控制@[green]if-else@[green]允许程序根据条件表达式的值进行分支, 并执行不同的代码块。 当程序执行到条件控制时,@[code]if()@[code]里的条件表达式的值将被测试, 如果测试通过,这个@[code]if()@[code]下面的代码块将被执行。 否则后面的@[code]else if()@[code](如果有的话)语句里的条件表达式将被 逐个测试,直到有一个成功通过测试,那么那个通过测试的@[code]else if()@[code] 语句下面的代码块将被执行。如果没有条件表达式通过测试,并且有这个条件控制里有 @[code]else@[code]语句,这个语句下面的代码块将被执行。 @[code] if( rand(10) > 7 ){ io.writeln( "greater than 7" ) }else if( rand(10) > 5 ){ io.writeln( "greater than 5" ) }else if( rand(10) > 3 ){ io.writeln( "greater than 3" ) }else{ io.writeln( "not greater than 3" ) } @[code] 在条件表达式之前可以有类似局部变量的表达式。 @[code] if( rnd = rand(100); rnd > 50 ) io.writeln( "random number:", rnd ); @[code] @[subsection] For循环控制 @[subsection] 道语言支持以下几种for循环结构: @[list] -- For-in 循环; -- 区间 for 循环; -- C风格三表达式 for 循环; @[list] For-in 循环通常被用于历遍容器类型对象里的元素。 最常用的就是历遍列表里的元素。 @[code] var numbers = { 1, 2, 3, 4, 5 } for(var num in numbers ) io.writeln( num ) @[code] 一个区间for循环依据指定区间开始值,结束值和可选的步长值来控制循环的执行。 @[code] for(var index = 1 : 2 : 10 ) { # step value = 2; io.writeln( index ) } for(var index = 1 : 10 ) { # step value = 1; io.writeln( index ) } @[code] 这两循环都将从"index=1"开始循环,每次循环,"index"的值都被加2或者1, 循环被重复直到"index"的值大于10才终止。 C风格的for循环是最灵活的循环结构。 @[code] for( init; condition; step ){ block; } @[code] 这种for循环的基本运行逻辑或步骤如下: @[list] == 执行初始表达式@[code]init@[code]; == 检查条件表达式@[code]condition@[code]; == 如果检查结果为真,执行下一步,否则退出循环; == 执行代码块@[code]block@[code]; == 执行表达式@[code]step@[code],并前往步骤2; @[list] The detailed steps may depends on the implementation but the basic execution logic is the same. 例子: @[code(dao)] for(i=0; i<3; ++i){ io.writeln( i ); } @[code(dao)] @[subsection] While循环控制 @[subsection] @[code]while@[code]是个简单的循环控制结构,它将重复执行一代码块, 直到它的循环条件表达式的值变假。 @[code] while( expression ){ block; } @[code] 如果@[code]expression@[code]的值为真,执行或重复执行@[code]block@[code] 直到@[code]expression@[code]的值变假。 @[code] var i = 0; while( i < 5 ){ io.writeln( i ); i += 1; } @[code] @[subsection] Do-While循环控制 @[subsection] @[code] do { block; } while ( condition ) @[code] 执行代码块@[code]block@[code],然后只要条件表达式@[code]condition@[code] 的值为真,重复执行@[code]block@[code]。 @[subsection] Switch-Case控制 @[subsection] Switch-case控制可以根据一个表达式多个可能的值选择不同的代码块进行执行。 它提供了一种方便的分支执行方式。 @[code] switch( expresssion ){ case C_1 : block_1 case C_2 : block_2 case C_3 : block_3 ... default: block0 } @[code] 这里如果@[code]expresssion@[code]的值等于@[code]C_i@[code], 那么代码块@[code]block_i@[code]将被执行; 这里@[code]C_i@[code]必须是常量,但它们不需要是同样的类型。 与C/C++不同,这里每个@[code]case@[code]的代码块被执行后都将自动跳出Switch-case结构, 不需要用到@[code]break@[code]语句。 如果你需要针对不同的case值执行同样的代码块,你可以将这些值放在一个case里: @[code] switch( expresssion ){ case C1, C2, C3 : block3 ... default: block0 } @[code] 这里是个简单的例子: @[code(dao)] var a = "a"; switch( a ){ case 1, "a" : io.write("case 1 or a"); default : io.write("case default"); } @[code(dao)] @[subsection] Break 和 Skip @[subsection] @[code]break@[code]可用来跳出循环,而@[code]skip@[code]则可用来跳过 一个循环里它后面的剩余部分,直接到下一步循环。 @[code]skip@[code]跟C/C++里的@[code]continue@[code]等价。 @[code] for(var i=0; i<5; ++i ){ io.writeln( i ); if( i == 3 ) break; } @[code] @[section] 函数 @[section] 函数是一相对独立的代码块,可在需要的地方和时候反复调用。 它可以接受参数以改变函数的行为。 它也可将结果返回给它的调用者。 @[subsection] 定义 @[subsection] 道函数可由关键字@[code]routine@[code]定义。例如: @[code(dao)] routine func( first, second ) { first += 10; second += "test"; return first, second; # 返回多个值; } var (ret1, ret2) = func( 111, "AAA" ); var ret3 = func( ret1, ret2 ); @[code(dao)] 这里定义了一个可带两个参数作为输入,并返回两个值作为输出。 @[subsection]参数类型与缺省值@[subsection] 道函数的参数可以有类型标注,也可以有缺省值: @[code] routine MyRout( name: string, index = 0 ) { io.writeln( "NAME = ", name ) io.writeln( "INDEX = ", index ) } @[code] 这里参数@[code]name@[code]的类型被声明为字符串, 参数@[code]index@[code]的缺省值被声明为零(它的类型也就被自动声明为整型)。 任何带缺省值的参数后面的参数必须都带缺省值。 @[subsection]函数重载@[subsection] 道语言里,函数可按参数类型进行重载。也就是可对于拥有不同参数类型的函数 使用同样的名称,函数调用时,道虚拟机根据调用中的参数选择正确的函数来运行。 @[code] routine MyRout( index: int, name = "ABC" ) { io.writeln( "INDEX = ", index ) io.writeln( "NAME = ", name ) } MyRout( "DAO", 123 ) # 调用上例中的MyRout() MyRout( 456, "script" ) # 调用此例中的MyRout() @[code] @[subsection]匿名函数和函数闭包@[subsection] 道语言里函数是基本类型。因此它们可在运行时创建并跟其他对象一样使用。 运行时创建的函数是匿名函数或函数闭包。 创建匿名函数或函数闭包的语法跟普通函数几乎等同,只有以下几点比较特殊: @[list] == 无函数名; == 参数缺省值不必是常量; == 它可以使用外部函数的局部变量; @[list] 如果一个被创建的函数需要访问外部局部变量,那么这个函数将以函数闭包的形式 创建,否则以匿名函数的方式创建。 下面是个简单的例子: @[code] var abc = "ABC"; var rout = routine( x, y: string, z = abc + abc ){ abc += y; io.writeln( "lambda ", abc ) io.writeln( "lambda ", y ) io.writeln( "lambda ", z ) } rout( 123, "XXX" ); io.writeln( abc ) # 输出: ABCXXX @[code] 要了解代码块方法(code section method)或函数修饰器(decorator), 请参看@[node]dao.routine.section@[node]和@[node]dao.routine.decorator@[node]。 要了解协程(coroutine)和产生器(generator),请参看@[node]module.core.coroutine@[node]。 @[section] 类 @[section] 类是一用户定义的数据结构,此数据结构支持数据抽象,封装,多态和继承等。 它通常被用来作面向对象的编程。 @[subsection]类定义@[subsection] 一个类的定义主要由数据成员和方法成员构成。 这些数据成员和方法成员定义了这个类的实力对象的状态和行为。 道语言类支持以下几种数据成员: @[list] --@[code]常量@[code]: 以@[code]const@[code]关键词声明; --@[code]静态变量@[code]: 以@[code]static@[code]关键词声明; --@[code]实例变量@[code]: 以@[code]var@[code]关键词声明; --@[code]实例定变量@[code]: 以@[code]invar@[code]关键词声明; @[list] 这些数据成员的声明可以有也可以没有类型标注,还可以有也可以没有初始化值。 这些类型标注和初始值声明跟函数的类型标注和缺省参数声明完全一样。 例如,下面的方式都可用来声明实例变量成员: @[code] var 变量; var 变量 = 初始值; var 变量: 类型名; var 变量: 类型名 = 初始值; @[code] 类的构造方法和普通成员方法必须使用@[code]routine@[code]关键词声明。 重载操作符的方法则必须使用@[code]operator@[code]关键词声明。 类成员的可访问性可由三个权限关键词声明: @[list] --@[code]public@[code]: 公共,无限制访问; --@[code]protected@[code]: 保护,仅可从本类和子类访问; --@[code]private@[code]: 私有,仅可从本类访问; @[list] 这里是个简单的例子, @[code] class ClassOne { var index = 0; var name : string var words : list = {} routine ClassOne( name :string, index = 0 ){ self.name = name; self.index = index; } } @[code] 在非静态类方法里,特殊变量@[code]self@[code]代表当前类实例对象。 另外类似C++,类方法可以在类定义体里声明,然后在类外部实现。 这种情况下,方法的原型在声明处和定义处必须完全一样。 @[subsection]类实例@[subsection] 类方法里与类同名的方法即为类构造方法。 类实例可由调用类构造方法产生。类构造方法的调用与普通函数调用完全一样。 @[code] var object = ClassOne( 'abc' ) @[code] 值得注意的是,类构造方法并不构造类实例,类实例实际上是在构造方法调用 开始之时就已产生,构造方法实际上是用来初始化该对象。 对于没有基类和构造方法的类,它的实例还可通过枚举它成员数据值的 方式构造。 @[code] class Point3D { var x = 0.0; var y = 0.0; var z = 0.0; } var point = Point3D.{ 1, 2, 3 }; @[code] 这种方式里,成员数据的名称也可被指定: @[code] var point = Point3D.{ y = 2, x = 1, z = 3 }; @[code] @[subsection]方法和属性@[subsection] 类支持三种典型的方法:静态方法,实例方法和实例常方法。 静态方法可直接通过类对象调用,因此这类方法里不能使用类实例成员变量。 其他两种方法都需要通过类实例调用。实例方法可访问和修改实例成员变量, 但实例常方法仅可以访问,而不能修改实例成员变量。 实例常方法可通过在参数列表括号后加"invar"关键词来声明。 @[code(dao)] class ClassTwo { private static state = 0 var value = 0 public static routine Stat() { state += 1 # 对! state += value # 错! 不能访问实例成员变量"value"! } routine Meth() { state += 1 # 对! value += 2 # 对! } routine InvarMeth() invar { state += value # 对! 可以访问实例成员变量"value"! value += 2 # 错! 但不可以修改"value"! } } @[code(dao)] 为了提供对上例中"value"变量的访问,@[code]GetValue()@[code]和@[code]SetValue()@[code] 这样的方法可以被提供。但更好的方式是使用属性方法。 使用属性方法访问保护或私有变量,可以在语法层面变得跟直接访问公共变量一样方便。 在道语言里,这可由重载象@[code].Name@[code]和@[code].Name=@[code]这样的操作符来实现。 例如: @[code(dao)] class MyNumber { private var value = 0; public routine MyNumber( init = 0 ){ value = init; } operator .value(){ return value } operator .value=( newval: int ) { value = newval; io.writeln( "value is set" ) } } var num = MyNumber( 123 ) num.value = 456 io.writeln( num.value ) @[code(dao)] @[subsection]继承@[subsection] @[code] class ColorRBG { var red = 0.0; var green = 0.0; var blue = 0.0; routine ColorRBG( R = 0.0, G = 0.0, B = 0.0 ){ red = R; green = G; blue = B; } } yellow = ColorRBG( 1, 1, 0 ); # create an instance. @[code] 下面将定义一个派生类: @[code] class ColorRGBA : ColorRBG { var alpha = 0.0; # alpha component for tranparency. routine ColorRGBA( R = 0.0, G = 0.0, B = 0.0, A = 0.0 ) : ColorRBG( R, G, B ){ alpha = A; } } var magenta = ColorRGBA( 1, 0, 1, 0 ); # not tranparent. magenta.alpha = 0.5; # change to half tranparency. @[code] 在定义派生类时,基类@[code]ColorRBG@[code]必须放在派生类名后面, 并用冒号分开。 在定义派生类的构造方法时,方法的参数可按例中传递给基类的构造方法。 道类还支持其他一些比较高级的特性,详情请参看@[node]dao.class@[node]。 @[text]