load help; @[name] dao.routine @[name] @[title] Routine 函数 @[title] @[text] 函数是一相对独立的代码块,可在需要的地方和时候反复调用。 它可以接受参数以改变函数的行为。 它也可将结果返回给它的调用者。 @[subsection] 定义 @[subsection] 道函数可由关键字@[green]routine@[green]定义。例如: @[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] 这里参数@[cyan]name@[cyan]的类型被声明为字符串, 参数@[cyan]index@[cyan]的缺省值被声明为零(它的类型也就被自动声明为整型)。 任何带缺省值的参数后面的参数必须都带缺省值。 @[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] @[text] @[name] dao.routine.closure @[name] @[title] 匿名函数和函数闭包 @[title] @[text] 道语言里函数是基本类型。因此它们可在运行时创建并跟其他对象一样使用。 运行时创建的函数是匿名函数或函数闭包。 创建匿名函数或函数闭包的语法跟普通函数几乎等同,只有以下几点比较特殊: @[list] == 无函数名; == 它可以使用外部函数的局部变量; @[list] 如果一个被创建的函数需要访问外部局部变量,那么这个函数将以函数闭包的形式 创建,否则以匿名函数的方式创建。 下面是个简单的例子: @[code] var rout = routine( name: string, value = 0 ){ io.writeln( name, value ) return name.size() + value } rout( "abc", 123 ) @[code] 这里与普通函数定义的唯一区别是没有函数名。 在上面例中的,被创建的函数没访问外层函数的局部变量,因此它被创建为普通的匿名函数。 这里将介绍另一例子,这个例子中被创建的函数将访问其外层函数的局部变量: @[code] routine MakeClosure( start: int ) { return routine( offset: int ){ return start + offset } } var rout = MakeClosure( 100 ); rout( 123 ) @[code] 这例中创建的函数访问了外层函数"MakeClosure()"的"start"局部变量, 那么这个被创建的函数将以函数闭包的形式创建。 当一个函数闭包被创建时,它所访问的外层局部变量将被存在被创建的函数中。 对于基本类型的局部变量,它们的值将被拷贝,而其它类型的变量,则仅被保存引用。 一旦函数闭包被创建,它们将不再需要它的创建环境就可使用。 匿名函数和函数闭包之间的主要区别在于是否有对外层函数局部变量的访问。 这个区别导致了它们的运行时的创建方式比较不一样: 函数闭包总是以新函数对象创建;而匿名函数则有可能返回同一个函数对象。 例如下面的匿名函数总返回同一个函数对象: @[code] routine MakeRoutine() { return routine( value: double ){ return value * value } } io.writeln( MakeRoutine(), MakeRoutine() ) @[code] 要这种情况发生,匿名函数不但不能访问外层函数的局部变量,也不能有静态变量。 如果有静态变量,它将跟函数闭包一样以新的函数对象创建。 例如下面的匿名函数创建每次都会生成一个不同的函数对象。 @[code] routine MakeRoutine() { return routine( value: double ){ static dummy = 123 return value * value } } io.writeln( MakeRoutine(), MakeRoutine() ) @[code] @[text] @[name] dao.routine.section @[name] @[title] 代码块方法 @[title] @[text] 代码块方法是一种特殊的函数或方法。对这种函数的调用需要附加一个代码块。 这个代码块相当于该函数调用的一个隐式额外参数。当这样的函数调用被执行时, 该函数将恰当地执行该代码块,并可能利用该代码块执行的结果。 并且这种代码块本身也类似一个函数,可以有参数,也可以有返回值。 其他语言里与道语言代码块方法最接近的是Ruby里的代码块方法。 不过在Ruby里,代码块方法的调用所附加的代码块被编译为函数闭包, 该闭包以隐式的额外参数传递给被调用的代码块方法。 道语言里代码块方法的代码块确实是个代码块,它存在于调用代码块方法的函数中。 也就是说,运行时没有产生函数闭包,而是直接在调用者的函数中定位并被执行。 这种代码块的优势是没有创建函数闭包的额外开销。 代码块方法是相对于其他语言里的函数式方法的一个更好的替代品。 它使得它的使用更直观自然。例如,它避免了在函数调用的参数里 写庞大的匿名函数或闭包,很大程度上增加了代码的可读性。 要定义一个代码块方法,用户需要定义两套参数和返回值: 一套为方法的普通参数和返回值;另一套为代码块的参数和返回值。 @[code] routine meth_name( meth_params ) [sect_params => sect_return] => meth_return { ... } @[code] 这里的参数列表@[code]sect_params@[code]表示什么类型的参数该代码块方法将传递给 它的代码块;而返回值类型@[code]sect_return@[code]则表示代码块应该返回什么类型的结果值。 代码块方法的调用如下: @[code] returned = meth_name( meth_params ) { code_block } @[code] 如果一个代码块方法不含普通的参数,那么它的调用可简单地变成: @[code] returned = meth_name { code_block } @[code] 缺省情况下,代码块可通过预先隐式定义的参数@[code]X@[code]和@[code]Y@[code] 接受代码块方法传递进的参数值。用户可以显式声明更有意义的参数: @[code] returned = meth_name { [index, item] code_block } @[code] 作为一个例子,列表类型有个可用来排序的代码块方法,它的原型如下: @[code] sort( self :list<@T>, k=0 ) [X :@T, Y :@T => int] => list<@T> @[code] 这里代码块的参数@[code]X@[code]和@[code]Y@[code]被用来传递列表的两元素值, 以便有代码块作比较。此代码块将依据代码块比较元素的结果作排序。 因此该@[code]sort()@[code]方法可按如下方式使用: @[code] var numlist = { 11, 44, 21, 32, 56, 67, 25 } # 按升序排序: numlist.sort { X < Y } # 按降序排序,直到最大的三个值被排好: numlist.sort( 3 ) { X > Y } # 现在numlist的前三个元素即最大的三个元素; var tuplist = { ( 2, 'ghi' ), ( 1, 'def' ), ( 2, 'abc' ), ( 1, 'abc' ) } tuplist.sort { # 先按元组的第一个元素排序: if( X[0] != Y[0] ) return X[0] < Y[0]; # 再按第二个元素排序: return X[1] < Y[1]; } @[code] 在用户定义的代码块方法里,@[code]yield@[code]语句可用来执行该方法被调用时所 附加的代码块。@[code]yield@[code]的参数将被传递给代码块,而代码块的返回值 则以@[code]yield@[code]的返回值返回。 下面是一个简单的用户定义代码块方法例子: @[code] # 一个可带代码块调用的函数。 # 代码块可接受一个整数为参数,并返回一个字符串。 routine Test() [X :int => string] => string { io.writeln( 'In functional method!' ); var s = yield( 123 ); # 执行代码块; io.writeln( 'Yielded value:', s ); return s; } Test { io.writeln( 'In code section:', X ); return 'abc'; } @[code] @[text]