淘先锋技术网

首页 1 2 3 4 5 6 7

五.函数

在lua中,若一个函数只有一个参数,并且此参数是一个字符串或table构造式,那么圆括号便可不写。

print "hello world"     --等同于print("hello world")
print {10,20}           --等同于print({10,20})

多重返回值:lua允许函数返回多个结果。根据情况,函数返回值的个数也不同

a.若将函数作为表达式的一部分来调用时,只保留函数的第一个返回值

b.当一个函数调用是一系列表达式中的最后一个元素(或仅有一个元素)时,才能获得函数的所有返回值

这里的"一系列表达式"在lua中表现为4种情况:多重赋值、函数调用时传入的实参列表、table的构造式和return语句


function foo0() end
function foo1() return "a" end
function foo2() return "a","b" end

---------------------------a----------------------
x,y = foo2(),20
print(x,y)                   --a 20
print(foo2().."x")           --ax
t = {foo2(),4}               --t[1]="a",t[2]=4

---------------------------b----------------------
--1.多重赋值
x,y,z,w = 1,foo2()
print(x,y,z,w)                 --1 a b nil


--2.函数调用时传入的实参列表
print(foo2())                --a b


--3.table的构造式
t = {foo2()}                 --t = {"a","b"}


--4.return语句
function foo()
	return foo2()
end
print(foo())                 --a b


--可以将函数放入一对圆括号中,从而迫使它只返回一个结果
print((foo2()))              --a

--函数unpack,接受一个数组作为参数,并从下标1开始
--返回该数组的所有元素

print(unpack({10,20,30}))    --10 20 30



变长参数:

--参数表中的...表示该函数可接受不同数量的实参
function add(...)
local sum = 0

for i,v in ipairs({...}) do
sum = sum + v
end

return sum
end

print(add(1,2,3,4))            --10


前面提到,函数是可以存储在变量中的。函数与其他所有值都是匿名的,即它们都没有名称。当讨论一个函数名时,实际上是在讨论一个持有某函数的变量。

a = {p = print}
a.p("hello woeld")     --hello woeld
print = math.sin
a.p(print(3.14159))    --约等于0
sin = a.p
sin(10,20)             --10 20

所以对于:

function foo(x) return 2*x end

其实就是等于:

foo = function (x) return 2*x end


--匿名函数的使用
--table库提供了一个函数table.sort,它接受一个table并对其排序
--它的第二个参数是一个匿名函数,该函数接受两个元素,并返回
--在有序情况下第一个元素是否应该排在第二个元素之前

a = {
{name = "aa",hp = "50"},
{name = "zz",hp = "30"},
{name = "cc",hp = "70"},
}

table.sort(a,function(a,b) return (a.name > b.name) end)
for i,v in ipairs(a) do
	print(v.name)        --zz cc aa
end



若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,这项特征称之为"语法域"。

假如有这样一个例子:

--根据年级对姓名进行排序
names = {"peter","paul","marry"}
grades = {marry = 10,paul = 7,peter = 8}

table.sort(names,function (n1,n2)
return grades[n1] > grades[n2]      
end)

for i,v in ipairs(names) do
print(v)                      --marry peter paul
end


现在要用一个函数来完成上面的排序,即对排序函数进行封装:

--根据年级对姓名进行排序
names = {"peter","paul","marry"}
grades = {marry = 10,paul = 7,peter = 8}

--[[table.sort(names,function (n1,n2)
return grades[n1] > grades[n2]      
end)--]]

function sortByGrade(names,grades)
	table.sort(names,function (n1,n2)
	return grades[n1] > grades[n2]
	end)
end

sortByGrade(names,grades)
for i,v in ipairs(names) do
print(v)                      --marry peter paul
end

可以发现sort的匿名参数可以访问grades,而grades是外部函数sortByGrade的局部变量。在这个匿名函数的内部,grades既不是全局变量也不是局部变量,将其称为一个"非局部的变量"。


再来看看这个:

function count()
	local i = 0
	return function()
		i = i + 1
		return i
	end
end

c = count()
print(c())                  --1
print(c())                  --2


在这段代码中,匿名函数访问了一个"非局部的变量"i,该变量用于保持一个计数器。初看上去,由于创建变量i的函数(count)已经返回,所以之后每次调用匿名函数时,i都应该是超出了作用范围的,但其实不然,lua会以closure(闭合函数)的概念来正确地处理这种情况。简单地讲,一个closure(闭合函数)就是一个函数加上该函数所需访问的所有"非局部的变量"。就以上例而言,closure(闭合函数)就是匿名函数加上变量i。对上例进一步修改:

function count()  
    local i = 0  
    return function()  
        i = i + 1  
        return i  
    end  
end  
  
c = count()  
print(c())                  --1  
print(c())                  --2  

c2 = count()  
print(c2())                 --1
print(c())                  --3
print(c2())                 --2  

因此c和c2是同一个函数所创建的两个不同的closure,它们各自拥有局部变量i的独立实例。

从技术上来讲,lua中只有closure,而不存在"函数",因为函数本身就是一种特殊的closure。不过只要不会引起混淆,仍将采用"函数"来指代closure。


非全局的函数:

由于函数是一种"第一类值",因此一个显而易见的推论就是,函数不仅可以存储在全局变量中,还可以存储在table的字段中和局部变量中。

a = {}

a.foo = function (x,y)
	return x + y
end

a.goo = function (x,y)
	return x - y
end

print(a.foo(1,2))                --3
print(a.goo(4,3))                --1

---------------------------------------------------

b = {
	foo = function(x,y) return x + y end,
	goo = function(x,y) return x - y end,
}

print(b.foo(1,2))                --3
print(b.goo(4,3))                --1

---------------------------------------------------

c = {}

function c.foo(x,y) return x + y end
function c.goo(x,y) return x - y end

print(c.foo(1,2))                --3
print(c.goo(4,3))                --1

只要将一个函数存储到一个局部变量中,即得到了一个"局部函数",也就是说该函数只能在某个特定的作用域中使用。



六.协同程序

协同程序跟线程差不多,区别在于:

一个具有多个线程的程序可以同时运行几个线程;

一个具有多个协程的程序在任意时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显式地要求挂起(suspend)时,它的执行才会暂停。

一个协同程序可以处于4种不同的状态:挂起(suspended)、运行(running)、死亡(dead)和正常(normal)。当创建一个协同程序时,它处于挂起状态,不会自动执行其内容。

lua将所有关于协同程序的函数放置在一个名为"coroutine"的table中:

函数create用于创建新的协同程序,它只有一个参数,就是一个函数。该函数的代码就是协同程序所需执行的内容。create会返回一个thread类型的值,用以表示新的协同程序。通常create的参数是一个匿名函数。

函数status可以用来检查协同程序的状态。

函数resume用于启动或再次启动一个协同程序,并将其状态由挂起改为运行。

co = coroutine.create(function ()
	print("hi")
end)

print(co)                         --thread: 002FC6B8
print(coroutine.status(co))       --suspended
coroutine.resume(co)              --hi
print(coroutine.status(co))       --dead

协同程序的强大之处在于函数yield的使用,该函数可以让一个运行中的协同程序挂起,而之后可以再恢复它的运行。

co = coroutine.create(function ()
	for i = 1,10 do               --默认递增1
		print("hi",i)
		coroutine.yield()
	end
end)

print(coroutine.status(co))       --suspended

coroutine.resume(co)              --hi    1
print(coroutine.status(co))       --suspended

coroutine.resume(co)              --hi    2
print(coroutine.status(co))       --suspended

当一个协同程序A唤醒另一个协同程序B时,协同程序A就处于一种特殊的状态,既不是挂起状态(A无法执行),也不是运行状态(是B在运行)。所以将此时的状态称为正常状态。


yield也是有返回值的:

co = coroutine.create(function (a,b,c)
	print("hi",a,b,c)
end)

coroutine.resume(co,1,2,3)              --hi 1 2 3
print(coroutine.resume(co,1,2,3))       --false	cannot resume dead coroutine

-------------------------------------------------------

co = coroutine.create(function (a,b)
	coroutine.yield(a + b,a - b)
end)

print(coroutine.resume(co,20,10))       --true 30 10
                                        --true表示没有错误
-------------------------------------------------------

co = coroutine.create(function ()
	print("hi",coroutine.yield())
end)

print(coroutine.status(co))             --suspended
coroutine.resume(co,2,3)                --无输出,因为被挂起了
print(coroutine.status(co))             --suspended
coroutine.resume(co,4,5)                --hi 4 5,yield的返回值是对应的resume的传入值
print(coroutine.status(co))             --dead

-------------------------------------------------------

co = coroutine.create(function ()
	return 6,7
end)

print(coroutine.resume(co))              --true 6 7