Lua函数的调用
在Lua中,函数是一种对语句和表达式进行抽象的主要机制,函数既可以完成某项特定的任务,也可以只做一些计算并返回结果。在第一种情况下,一句函数被视为一条语句:而在第二种情况下,则将其视为一句表达式。
print(9*9,9/8)
a = math,sin(3) + math.cos(10)
print(os.date())
无论函数采用哪种用法都需要将所有参数放到一对圆括号中,即使调用函数时没有参数,也必须写出一对空括号。对于此规则只有一种特殊的情况例外:一个函数若只有一个参数,并且此参数是一个字面字符串或者table构造式,那么圆括号便是可有可无的,见下面例子:
print “Hello World " <=> print("Hello World")
dofile 'a.lua' <=> dofile('a.lua')
print [[a multi-line <=> print([[a multi -line
message]] message]]
f {x=10, y=20} <=> f({x=10,y=20})
type{} <=> type({})
Lua函数参数传递
原则:若实参多余形参,则舍弃多余的实参,若实参不足,则多余的形参初始化为nil
例子:
function f(a,b) return a or b end
在以下的几种调用中,实参与形参的对应关系为:
调用 形参
f(3) a = 3 ,b = nil
f(3,4) a = 3,b = 4
f(3,4,5) a = 3, b = 4 (5被丢弃了)
Lua函数多重返回值
Lua函数允许存在多个返回值。若Lua函数存在多重返回值,则需要采用多重赋值的语句来接受函数的返回值。
exp:
s,e = string.find("hello Lua users" , "Lua")
print(s,e) ---> 7 9
Lua会调整一个函数的返回值数量以适应不同的调用情况,若将函数调用作为一条单独语句时,Lua会丢弃函数的所有返回值。若将函数作为表达式的一部分来调用时,Lua只保留函数第一个返回值。只有当函数调用是一系列表达式中的最后一个元素(或仅有一个元素)时,才能获得它的所有返回值。这里的所谓的“一系列表达式”在Lua中表现为四种情况:1、多重赋值、2、函数调用时传入实参列表、3、table的构造式和4、return语句。接下来举例这几种情况:
假设有如下函数定义:
function foo0() end --无返回值
function foo1() return “a”end --返回一个结果
function foo2() return "a","b" end --返回两个结果
1、在多重赋值中,若一个函数调用是最后的(或仅有的)一个表达式,那么Lua会保留其尽可能多的返回值,用于匹配赋值变量:
x,y = foo2() --x = "a", y="b"
x = foo2() -- x= "a" ,"b"被丢弃
x , y , z = 10, foo2() -- x = 10, y = "a" z = "b"
如果一个函数没有返回值或者没有返回足够多的返回值,那么Lua会用nil来补充缺失的值:
x , y = foo0() -- x = nil, y = nil
x , y = foo1() -- x = "a" , y = nil
x,y,z = foo2() -- x= "a" , y = "b" , z = nil
如果一个函数调用不是一系列表达式的最后一个元素,那么将只产生一个值:
x, y = foo2(),20 --x = "a" ,y = 20
x,y = foo2(), 20 , 30 -- x = nil, y = 20, 30 将被丢弃
2、当函数调用作为另一个函数调用的最后一个(或仅有的)实参是,第一个参数的所有返回值都将作为实参传入第二个函数。例子:
print(foo0()) -->
print(foo1()) -->a
print(foo2()) -->a b
print(foo2(),1) --> a 1
print(foo2() .. "x") -->ax (见下面说明)
说明:当foo2()出现在一个表达式中时,Lua会将其返回值数量调整为1.因此在上面例子最后一行中只有"a"参与了字符串连接操作。
print函数可以接受不同数量的实参。例如在f(g())调用中,f具有固定数量的实参,如之前 所看到的,Lua会将g的返回值个数调整为f的实参个数。
3、table构造式可以完整的接收一个函数调用的所有结果,即不会有任何数量方面的调整:
t = {foo0()} -- t = {} (一个空的table)
t = {foo1()} --t = {“a”}
t = {foo2()} --t = ("a", "b")
不过,这种行为只有当一个函数调用作为最后一个元素才会发生,而在其他位置上的函数调用总是产生一个结果值:
t = { foo0,foo2(),4} -- t[1] = nil, t[2] = "a", t[3] = 4
4、最后一种情况是return语句,诸如return f()这样的语句将返回f的所有返回值:
function foo (i)
if i == 0 then return foo0()
else if i == 1 then return foo1()
else if i == 2 then return foo2()
end
end
print(foo(1)) -->a
print(foo(2)) -->a b
print(foo(0)) --(无返回值)
print(foo(3)) --(无返回值)
也可以将一个函数调用放入一对圆括号中,从而迫使它只能返回一个结果
print((foo0())) -->nil
print((foo1())) -->a
print((foo2())) -->a
注意:return语句后面的内容是不需要圆括号的,在该位置上书写圆括号会导致不同的行为。例如return(f(x)),将只返回一个值,而无关乎f返回了几个值。
unpack函数
关于多重返回值还要介绍一个特殊的函数--unpack,它接受一个数组作为参数,并从下标1开始返回给数组的所有元素:
print(unpack({10,20,30}) -->10 20 30
a,b = unpack({10,20,30}) -- a = 10, b =20 , 30 倍丢弃
unpack的一项重要用途体现在“泛型调用(genericcall)”机制中。泛型调用机制可以动态地以任何实参来调用任何函数。举例来说,在ANSI中是没用办法编写泛型调用的代码。最多是声明一个能接受变长参数的函数(通过stdarg.h),或者使用一个函数指针来调用不同的函数。并且在C语言中,无法再同一次函数调用中传入动态数量的参数。也就是说,在每次调用函数时必须传入固定数量的参数,并且每个参数都具有确定的类型。然而在Lua中就可以做到这点。如果想调用任意函数f,所有的参数都在数组a中,那么可以这么写:
f(unpack(a))
unpack将返回a中所有的值,这些值将作为f的实参。举例来说,假设执行:
f = string.find
a = {"hello",“ll”}
f(unpack(a))将返回3和4,这与直接调用string.find("hello","ll")所返回的结果是一模一样的。unpack函数采用C语言实现,但是可以通过Lua递归实现unpack函数:
function unpack (t, i)
i = i or 1
if t[i] then
return t[i],unpack(t,i+1)
end
end
第一次调用是只传入一个实参,此时i为1,。然后,函数返回t[1]与unpack(i,2)调用的结果,后者又将返回t[2]与unpack(t,3)的结果,依次类推,直至遇到一个nil元素为止,停止递归。
Lua函数变长参数
1、Lua变长参数通过“...”表达式实现。
exp:
function add( ... )
local a = 0
for i, v in ipairs({...}) do
s = s + v
end
return s
end
print(add(3,4,10,25,12)) -->54
2、也可以通过变量来读取"..."表达式,"..."表达式类似于一个具有多重返回值的函数,它返回的是单钱函数的所有变长参数,例如:
local a,b = ...
3、固定长度参数可以和变长参数同时使用,但是固定参数必须放在变长参数之前,Lua会将前面的实参赋予固定参数,而将余下的实参(如果有的话)视为变长参数。:
exp:
function fwrite(fmt , ...)
return io.write(string.format(fmt, ... ))
end
4、通常一个函数在遍历其变长参数时只需要使用表达式{...},就可以向访问一个table一样,访问所有的变长参数。如果变长参数中存在一些故意传入的nil,那么此时就需要函数select来访问变长参数了。调用selector时必须传入一个固定实参selector(选择开关)和一系列变长参数。如果selector为数字n,那么select返回它的第n个可变实参;否则,selector只能为字符串“#”,这样select会返回变长参数的总数。
exp:
for i= 1,select(‘#’,...) do
local arg = select(i,...) --得到第i个参数
<循环体>
end
Lua函数具名实参
Lua函数参数传递都是通过位置来传递的,也就是说,在调动一个函数时,实参时通过它在函数表中的 位置与形参匹配起来的。第一个实参 的值与第一个形参相匹配,一次类推。但有时通过名称来制定实参很有用。
错误的使用方式:
rename(old = “temp.lua” , new = "temp1.lua")
Lua不支持这种语法,但是可以通过一种细微的改变来实现相同的效果。
将所有的实参组织到一个table中,并将这个table作为唯一的实参传给函数。另外,还需要用到一种Lua中特殊的函数调用语法,就是实参只有一个table构造式时,函数调用中的圆括号是可有可无的:
rename{old = “temp.lua” , new = "temp1.lua"}
另一方面,将rename改为只接受一个参数,并从这个参数中获取实际的参数:
function rename(arg)
return os.rename(arg,old,arg.new)
end
函数是第一类值
函数closure,函数闭包是包含函数这个第一类值和函数中的非局部变量的。
--function numAcount()
--local i = 0;
-- return function ()
-- i = i+1
-- return i
-- end
--end
--num = numAcount()
--print(num()) --1
--print(num()) --2
--print(num()) --3
--num1 = numAcount()
--print(num1()) --1
--print(num1()) --2
--print(num1()) --3
计数器的实现,如上面代码
                  
                  
                  
                  
      
          
                
                
                
                
              
                
                
                
                
                
              
                
                
              
            
                  
被折叠的  条评论
		 为什么被折叠?
		 
		 
		
    
  
    
  
            


            