Lua学习笔记(三)

Lua程序可以使用Lua编写的函数,也可以使用C语言编写的函数,调用时没区别。函数需要将所有参数放在一个圆括号内。但是如果函数只有一个参数,且参数为字面字符串或table构造式,圆括号可以省略。关于面向对象部分,Lua也提供冒号操作符,例如func.f(func, x),可以写作:func:f(x),此时,func隐式地作为函数的第一个参数。

函数以function定义,一个函数定义中包括名称,参数表和函数体,举书上的例子说明:

function add(a)
  local sum = 0
  for i,v in ipairs(a) do
    sum = sum + v
  end
  return sum
end

例子中,add是函数名称,a是参数(此处只有一个参数),end之前的部分为函数体。

关于函数的参数概念,这里还是要说一下。如同C,有形参和实参的概念。形参,即形式参数,也就是函数定义的时候参数表的部分,例如上面的例子,a就是形参。实参就是实际参数,是调用函数时传入的,例如上面的例子,我要调用这个add函数,那么调用时候赋予的参数(此处为table)就是实际参数,这里省略该例。如果实参和形参的数量不同,Lua会做自动调整。原则是:实参多于形参,舍弃多余的实参;实参少于形参,多余的形参初始化为nil。我们来看例子:

function f(a,b)
return a or b
end


f(3)
f(3,4)
f(3,4,5)

--[[
f(3)的调用,实参小于形参,此时a = 3, b = nil
f(3,4)d的调用,实参和形参相等,此时a = 3, b = 4
f(3,4,5)的调用,实参大于形参,此时a = 3, b = 4, 多于的实参5被丢弃
--]]

下面,我们来看几个函数的特征:

1.多重返回值

Lua允许函数返回多个结果,这个很好理解,我们来看书上列出的例子即可理解:

function maximum(a)
  local mi = 1
  local m = a[mi]
  for i,val in ipairs(a) do
    if val > m then
      mi = i 
      m = val
    end
  end
  return m, mi
end

print(maximum({8,10,23,12,5}))


-- 返回结果:23   3

例子中我们定义了一个找最大值的函数maximum,传入实参为一个数值集组成的table,形参a接受了这个table,进入函数体的泛型for循环,记录下最大值以及最大值所在的位置。之后return返回这两个参数,因此,print接收到的是两个值,分别为最大值及其位置,输出结果。这里要注意的是,如果将函数作为表达式的一部分来调用,Lua只能保留第一个返回值,只有当一个函数调用是一系列表达式中的最后一个元素(或仅有一个元素),才能获得所有返回值

以下代码段较长,介绍上述特点:

--定义三个函数用于做例子
function f0()
end

function f1()
  return "a"
end

function f2()
  return "a", "b"
end

--1.多重赋值情况
x,y = f2()
x = f2()
x,y,z = 10, f2()

x,y = f0()
x,y = f1()
x,y,z = f2()

x,y = f2(), 20
x,y = f0(), 20, 30

--上例中,若对所有结果print,可以得到:
--[[
第一个x = "a", y = "b"
第二个x = "a", b无匹配的赋值,舍弃
第三个x = 10, y = "a", z = "b"
这一组是因为f2的调用是一系列表达式中的最后一个元素,lua则会保留尽可能多的返回
--]]
--[[
第四个x = nil, y = nil
第五个x = "a", y = nil
第六个x = "a", y = "b", z = nil
这一组是因为f0没有返回值,f1有一个返回值,f2有两个返回值,都不能满足等号左边被赋值的数量,因此,lua使用nil替补不足
--]]
--[[
第七个x = "a", y = 20
第八个x = nil, y = 20, 30无匹配的赋值,舍弃
这一组由于f2不是一系列表达式中的最后一个元素,那么只能返回一个值
--]]



--2.函数调用时传入的实参列表
print(f0())
print(f1())
print(f2())
print(f2(), 1)
print(f2() .. "x")

--[[
输出结果:
第一个为空,因为f0什么也没有
第二个为a,因为f1只返回一个a,并且它是print调用时传入的实参列表中的最后一个元素,所有返回值都传入
第三个为a  b,因为f2只返回一个a,并且它是print调用时传入的实参列表中的最后一个元素,所有返回值都传入
第四个为a  1,因为f2不是print调用时传入的实参列表中的最后一个元素,因此自传回一个参数a
第五个为ax,因为f2作为表达式的一部分来使用,lua只保留第一个返回值
--]]



--3.table的构造式
t0 = {f0()}
t1 = {f1()}
t2 = {f2()}
t = {f0(), f2(), 4}

--[[
t0={},为空表,因为f0为空的
t1={"a"},因为f1返回了a,并且它是table构造式的最后一个元素,因此返回所有值
t2={"a", "b"},因为f2返回了a和b,并且它是table构造式的最后一个元素,因此返回所有值
t={nil,"a",4},因为f0返回空,因此赋予nil值,f2返回a和b,但不是table构造式的最后一个元素,因此返回第一个结果a
--]]



--4.return语句
function f(i)
  if i == 0 then
    return f0()
  elseif i == 1 then
    return f1()
  elseif i == 2 then
    return f2()
  end
end

print(f(1))
print(f(2))
print(f(0))
print(f(3))

--[[
因为f0,f1,f2都是return语句的最后一个元素,因此,打印出他们各自的返回值,由于不存在f3,因此无返回
--]]

下面来看第二个特征:

2.变长参数

即函数可以接受不同长度的实参,在函数中体现为参数表用三个点表示(…),来看例子:

function add(...)
  local s = 0
    for i,v in ipairs{...} do
      s = s + v
    end
  return s
end

print(add(3,4,10,25,12))
print(add(7,19,33))
print(add(2,4,11,18,20,22,27))

--输出结果:54,59104

上例中使用了变长参数,add在print中第一次调用,传入的5个参数都被接纳,并通过泛型for做累加运算,此时,ipairs后的三个点成了表达式,在函数体中要运用传入的参数,就需要则三个点的表达式,最后返回累加结果。add的第二次和第三次分别传入了三个参数和七个参数,结果依然计算准确。因此lua的这个特性使得函数的使用和编写非常灵活。我们应该掌握,提高效率。

这里要注意的是也可以将固定参数和变长参数混合使用,但是固定参数要在变长参数之前,来看格式化文本和输出文本组成的例子:

function fwrite(fmt, ...)
  return io.write(string.format(fmt, ...))
end

fwrite()
fwrite("a")
fwrite("%d%d", 4, 5)


--[[
第一次调用,fmt = nil, 无变长参数
第二次调用,fmt = "a", 无变长参数
第三次调用,fmt = "%d%d", 变长参数为45
--]]

Tips:使用select遍历函数的所有变长参数(当变长参数中含有故意传入的nil时,应该使用此方式)

for i = 1, select('#', ...)
  local arg = select(i, ...)
  --body
end

上例中select(‘#’, …)返回所有变长参数总和(包括nil),select(i, …)返回第i个变长实参。select的调用要注意的是必须有一个固定参数(数字或#)以及变长参数。

3.具名实参

Lua中,调用一个函数时,实参通过其在参数表中的位置与形参相匹配,因此具有位置性。具名实参的意思是通过名称来指定实参,可以不在乎其位置。

看一个书上的例子,一个用于创建窗口的函数拥有大量的参数,而有些是可选的,使用具名实参。

--新建窗口所必需的参数检查函数Window
function Window(options)
  if type(options.title) ~= "string" then
    error("no title") --检查标题是否存在
  elseif type(options.width) ~= "number" then
    error("no width") --检查宽度是否存在
  elseif type(options.height) ~= "number" then
    error("no height") --检查高度是否存在
  end

--函数_Window用于创建新窗口,位于检查函数Window中,上述检查通过后给予相应赋值
  _Window(options.title, options.x or 0, options.y or 0, options.width, 
          options.height, options.background or "white", options.border)
end


w = Window{x = 0, y = 0, width = 300, height = 200,
           title = "Lua", background = "blue",
           border = true}

--经过w的调用,Window检查通过,将所有参数按参数名称赋予_Window,完成窗口创建,而不需要关注参数的顺序

该例子为展示用例,单独运行是不通过的,因为没有初始化_Window,这里只是做一个介绍。给参数命名是一种良好的习惯,便于调用,应该要掌握这个技巧。

以上就是函数部分的学习,这部分笔记到此结束,下次我们学习更深层次的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值