使用函数参数
形参、实参
形参:函数定义时被包含在括号中具名参数,形参名也是局部有效的变量名字。
实参:函数调用时括号中包含的实际参数,实际参数是一个右值表达式。
//这里的a,b,c称为形参,可以将形参看成函数内部的局部变量名字
function test(a,b,c){ return a+b+c; //形参可以在函数体内部作为局部变量使用 }
返回值= test(2,3,4); //这里的2,3,4 称为实参
实参的数目如果多于形参的数目,多余部分被丢弃。
实参的数目如果少于形参的个数,不足的部分添加null值。
请参考:调用函数
可变参数
1、在形参列表尾部定义可变参数
在定数定义的形参列表尾部,可以用三个连续的圆点表示可变参数(不定参数)。
可变参数必须是最后一个参数,如下:
io.open()
func = function( a,b, ... ){ //使用三个连续的圆点表示任意个数、任意类型的参数
var arg = { ... }
io.print( #arg + "个参数", arg[1],arg[2] )
}
func(1,2,99,888,777);
2、在实参列表尾部使用可变参数
在实参列表尾部可以使用可变参数
io.open() str ="abcd" //在实参尾部可以使用不定参数 io.print( "string.unpack有多个返回值",string.unpack(str) ); //可变参数不放在最后面就只能取第一个值 io.print( string.unpack(str) ,"string.unpack的第一个返回值");
在形参中指定默认值参数
实参可以省略,省略的实参传递null空值。如下:
io.open(); //打开控制台 io.print( , ,2,3,) //下面都是等价的写法 io.print( null , null , 2 , 3 , null ) //与上面一行的作用相同 io.print( null , null , 2 , 3 ) //与上面一行的作用类似
在函数定义的形参表中,可以为任意参数指定默认值.
如果省略相应实参,或实参为null值时,如果参数指定了默认值,则将默认值作为实参。
示例如下:
io.open(); //打开控制台窗口 //在函数定义的形参中,使用等号指定形参的默认值; function func(a,b=123,c="字符串",d=2,e=true) { io.print(a,b,c,d,e) } //调用函数 func(1,,,4,5 )
形参默认值仅允许使用以上示例中演示的字面值常量(数值、字符串、布尔值),不可使用其他变量或表达式。
在函数实参中构造表
当函数的所有实参由键值对组成,并由分号分隔,aardio将实参构造为table对象。
示例:
//打开控制台窗口 io.open(); function func(tab,a,b){ io.print(tab,a,b); } //此时 形参tab= 1, 形参a= 2, 形参b=3 func( 1,2,3); //aardio将下面这句代码解析为 func( { k=1 ; k2 = 2 } ) func( k=1 ; k2 = 2 );
构造表作为函数参数,可以增强代码的可读性。例如:
函数调用 ( 名字 = "某某"; 住址 = "北京"; 家庭成员 = 统计函数( 男 = 6, 女 = 3, 老 = 1, 幼 = 1, ) );
table参数的副作用
1、纯函数
在aardio中函数是纯函数(Pure Function)- 输入输出数据流都是显式(Explicit)的。
函数从函数外部接受的所有输入信息都通过参数传递到该函数内部;函数输出到函数外部的所有信息都通过返回值传递到该函数外部,即函数的数据只有一个入口( 参数 ),一个出口( 返回值 )。函数可以返回多个值。
aardio的函数只有输入参数,没有输出参数,不能在函数体中改变实参的数据。
API函数中的输出参数同样被转换为返回值 - 然后附加到返回值列表中。
2、具有副作用的table参数
在虚函数中传址参数仍然具有副作用,table对象在赋值或传参中都是传址的,传递过程中并不改变指向的对象,而table的成员又是可修改的,这就使table对象在函数的传参过程中具有副作用,对table参数的成员进行修改会作用到外部对象,修改的作用域不局限于函数体内部。
如果需要在函数内部改变外部变量的值,那么应当把数据封装到一个table对象中。
function set(t) { //table参数具有副作用 t.x = 256; //在这里可以改变table参数对象的成员值,并作用到函数外部 //t不是输出参数,下面的代码仍然不能改变外部table对象 t = 123;//请注意输出参数针对的是参数本身,而副作用影响的是参数指向的数据 } tab ={x=10,y=20} set(tab); //table参数是按引用传递的 io.open(); //打开控制台窗口 io.print(tab.x); //显示256,tab.x被函数改变了
这个有一点利用潜规则走后门的意思,不声不响的修改了外部的对象,又没有显式的交待,应当尽量避免这么做。当然,没有必要默守成规,可以使用更友好的函数命名,清晰的注明副作用。例如:
import win; point = ::POINT(12,23); ::ClientToScreen(hwnd,point);//ClientToScreen的函数清晰的注明了它会改变point中的坐标 result,point = ::ClientToScreen(hwnd,point);//这是更推荐的写法
有经验的程序员总是不断提醒我们,多打几个字,减少大堆的麻烦!
输出参数
虽然从语法上说,aardio函数没有定义输出参数的这个概念,但是aardio函数支持在函数中返回多个返回值,通过返回值也可以实现修改输出参数的值,举个例子:
test = function(x,y){ x = x + 1; y = y + 1; return x,y; } var x,y = 1,2; //这里输入参数是 x,y,通过返回值接收了x,y的新值 x,y = test(x,y);
aardio 在调用外部 COM 函数,或者外部 API 函数时,同样是使用上面的方法来支持输出参数。
例如调用COM函数时,所有输出参数都会增加一个返回值(按参数的前后位置排序返回),而调用外部API函数时,是同样的,所以在API函数声明中定义为输出参数的参数(类型后面加&,例如int &) - 都会增加一个对应的返回值。
如果在 aardio中定义外部COM函数或外部API函数需要用到的回调函数时,同样使用多个返回值来修改输出参数的值。
例如,在调用API函数时定义stdcall回调函数,下面的x就是输出参数:
stdCallback = raw.tostdcall( function(x){ x = x + 1; return 123,x; },"int(int &x)" )
或者在调用COM控件时定义各种回调函数(例如定义 COM事件),例如在使用web.form时定义NewWindow3事件:
wb.NewWindow3 = function(ppDisp,cancel,dwFlags,bstrUrlContext,bstrUrl ) { //这里ppDisp,cancel都是输出参数,下面修改了cancel的值为true return ppDisp,true; };
owner参数
请参考:owner