在lua中,函数是语句和表达式体现的主要机制。函数可以完成某些特定的任务、计算和返回执行结果。

前者当成一个语句,后者当成一个表达式:

 print(*,/)
a = math.sin() + math.cos()
print(os.date())

在这两种情况下,调用函数都要用一个括号携带一系列参数去表示。如果函数不带参数,也需要用空的括号去表示。

但是有几个特列括号是可以省略的:

print "Hello World"            --函数只有一个字符串参数
print [[ a multi-line message ]] f{x=,y=} --参数是一个表
type{}

  Lua为针对面向对象的调用提供了特殊的语法——冒号操作符。

o.foo(o,x)可以用o:foo(x)表示,这样去调用foo,是隐含地把o作为函数第一个参数。

一个Lua程序可以使用Lua或C定义的函数。就像所有的标准Lua库是用C写的一样。

然而,在调用一个函数时,两者是没有区别的。

一个函数定义的常规语法像这样:

-- add the elements of sequence 'a'
function add (a)
local sum =
for i=, #a do
sum = sum + a[i]
end
return sum
end

可以看到,一个函数定义由函数名(add)、参数(a)、函数体(一系列的语句)组成。

函数参数是当作局部变量使用,由函数调用时的参数去初始化。

当调用函数时的参数不等于函数定义时的参数个数时,Lua会自动调整数量。类似于多重赋值。

这种调整行为其实很有用,比如对于默认实参的应用,以下是一个全局计数器:

function incCount(n)
n = n or
count = count + n
end

该函数用1作为它的默认实参,每次不带参数去调用incCount就增加1。

5.1 多重返回值

  Lua函数有一个与众不同的特点,就是返回多个值。Lua中有几个预定义函数就是多重返回值。

string.find 函数:

s,e = string.find("hello Lua users","Lua")
print(s,e) --> 7 9

如果该函数匹配到了Lua,它就返回两个索引值,分别指向“Lua”在字符串中的开始和结束位置。

用Lua写的函数同样可以返回多个结果,只需在return后面给出。

例如,找出一个序列中最大值,并返回它所在的位置:

function maximum (a)
local mi =
local m = a[mi]
for i = , #a do
if a[i] > m then
mi = i; m = a[i]
end
end
return m , mi
end print(maximum({,,,,})) -- 23 3

  Lua也会调整函数返回的个数,以适应不同的调用环境。

当函数作为一个单独的语句调用时,Lua会丢掉所有函数返回。

当函数作为一个表达式被调用时,Lua只保留第一个返回值。

以下情况,函数调用处在最后的表达式中时,才能获得函数所有值:

函数作为语句用于多重赋值、 

函数作为实参被调用、

table构造、

return语句、

有以下函数:

function foo0() end
function foo1() return "a" end
function foo2() return "a","b" end
x,y   = foo2()    -- x="a",y="b"
x = f002()    -- x="a","b"被丢弃
x,y,z = ,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(),        --x="a",y=20

当函数作为作为最后一个参数或只有它一个参数被调用时,函数返回所有的结果:

print(foo0())        -->空
print(foo1()) -->a
print(foo2()) -->a b
print(foo2(),) --> a 1
print(foo2() .. "x") -->ax

table构造时,可以接收一个函数的所有结果

t = {foo0()}        --t={}  空table
t = {foo1()} --t = {"a"}
t = {foo2()} --t = {"a","b"} --但是函数不是最后一个元素或唯一一个元素时,它只返回一个值
t = {foo0(),foo2(),} --t[1]=nil,t[2] = "a",t[3] = 4

最后,return语句,return f()  返回所有的 f 函数的值:

function foo (i)
if i == then return foo0()
elseif i == then return foo1()
elseif i == then return foo2()
end
end print(foo()) -->a
print(foo()) -->a b
print(foo()) -->no results
print(foo()) -->no results

也可以将一个函数调用放入一对括号中,强制返回一个结果:

print((foo0()))        -->nil
print((foo1())) -->a
print((foo2())) -->a

还有一个特殊的函数——unpack,它接收一个数组作为参数,并从下标1开始返回该数组的所有元素:

pirnt(unpack{,,})  -->10  20  30
a,b = unpack{,,}  -->a=10,b=20,30被抛弃

unpack函数的一个重要用途体现在“泛型调用”,即可以动态地以任何参数来调用任何函数。

如果想调用函数fun,而所有参数都在数组a中:

f = string.find
a = {"hello","ll"}
print(f(table.unpack(a))) -->等价于string.find("hello","ll")
-->3 4

通常,unpack可以通过取长度操作符"#"知道有多少个元素返回,也可以通过提供确切的限制:

print(table.unpack({"Sun","Mon","Tue","Wed"},,)  --返回索引2到索引3的值
--> Mon Tue

尽管unpack是由C写的,但是也可以用Lua通过递归来实现:

function unpack(t,i,n)
i = i or
n = n or #t
if i<= n then
return t[i],unpack(t, i+, n)
end
end

当第一次只用一个参数调用时,i = 1 ; n = #t,然后return t[1] ,紧接着返回所有unpack的值。unpack(t,2,n)...unpack(t,3,n)....

直到最后一个n元素。

5.2 变长参数

  Lua中的函数是可变参的,它可以接收不同的数量的参数。

例如下面的例子,下面的函数返回所有参数的总和:

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

"..." 表示这个函数是可变参函数。函数体内可以再次使用它表示变参。

可以把"..."表达式称作变参表达式,类似于多重返回值函数。

local a,b = ...

同样的,上面的命令创建了两个用变参的前两个参数初始化的变量。

实际上,可以通过变长参数模拟Lua中普通的参数传递机制:

function foo(a,b,c)

可以转换为:

function foo(...)
local a,b,c = ...
end

下面的函数,它是一个多值恒定函数,简单地返回了所有的参数:

function id(...)
return ...
end

来看看另一个很有用的例子。Lua提供了一个格式化文本函数(string.format)和一个文本输出函数(io.write)。

将两个结合到一个变参函数里:

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

注意在”...“前有一个fmt存在,可变参函数可以有任意数量的固定参数,必须放在变参前。

调用                   参数
fwrite() fmt = nil, 没有变参
fwrite("a") fmt = "a", 没有变参
fwrite("%d%d",,) fmt = "%d%d",变参=,

为了遍历变参,函数可以使用{...}把变参收集到一个table里。

在一些特殊的场合,变参中可能有一些nil,Lua提供了一个table.pack函数。

该函数接收变长的参数,并且返回一个由变参组成的table表。这个表有一个隐含的n元素,代表这个表的参数总个数。

注意,这个表返回的不一定是一个序列。

下面的函数用table.pack去检测它的参数是否含有nil:

function nonils(...)
local arg = table.pack(...)
for i = i,arg.n do
if arg[i] == nil then
return false
end
end
return true
end print(nonils(,,nil)) -->false
print(nonils(,)) -->true
print(nonils()) -->true
print(nonils(nil)) -->false

然而请记住,当变参中没有nil时,{...}要比table.pack方便快捷。

5.3 具名实参

  Lua中的参数传递是具有位置性的,调用函数时,参数是按照位置进行匹配。

用os.rename来进行阐述,该函数的作用是改变文件名。通常你会不记得哪个在前,哪个在后。

因此,会希望这个函数能接受两个具有名称的实参,例如:

-- invalid code
rename(old = "temp.lua", new = "temp1.lua")

Lua不支持这种语法,但可以稍加改变达到该效果。用table传参。

rename{old = "temp.lua",new = "temp1.lua"}

于是,函数定义如下:

function rename(arg)
return os.rename(arg.old,arg.new)
end

当函数有很多可选参数的时候,这种方式传参非常有用。

比如,一个GUI库创建一个窗口的函数,就有很多参数,并且很多参数是可选的。

最好的做法就是用具名实参:

function window (options)
-- check mandatory 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 -- everything else is optional 可选参数
_window (options.title,
        options.width,
        options.height,
options.x or ,         --default value
options.y or ,         --default value
options.background or "white",   --default
options.border         -- default is false (nil)
)
end w = window{ x= ,y = , width = ,height = ,
title = "Lua" , background = "blue",border = true
}

window可以自由检查必要参数,也可以添加默认参数。

_window函数实际才是需要所有正确参数来创建一个新窗口。

chapter5 函数的更多相关文章

  1. 《Linux内核设计与实现》CHAPTER5阅读梳理

    <Linux内核设计与实现>CHAPTER5阅读梳理 [学习时间:2.5hours] [学习内容:系统调用的概念.功能及实现:系统调用的创建和使用方法] CHAPTER5 系统调用 1.系 ...

  2. ###《Effective STL》--Chapter5

    点击查看Evernote原文. #@author: gr #@date: 2014-09-17 #@email: forgerui@gmail.com Chapter5 算法 Topic 30: 确保 ...

  3. JavaScript 高级程序设计(第3版)笔记——chapter5:引用类型

    Chapter5 引用类型 本章内容: l  使用对象 l  创建并操作数组 l  理解基本的JavaScript类型 l  使用基本类型和基本包装类型 l  从技术上讲,JavaScript是一门面 ...

  4. 【Linux_Unix系统编程】chapter5 深入探究文件IO

    Chapter5 深入探究文件I/O 本章节将介绍另一个与文件操作相关的系统调用:多用途的fcntl(),并展示其应用之一读取和设置打开文件的状态标志. 5.1 原子操作和竞争条件 所有系统调用都是以 ...

  5. 【Unix网络编程】 chapter5 TCP客户,服务器程序实例

    chapter5 5.1 概述 5.2 TCP回射服务器程序:main函数 int main(int argc, char **argv) { int listenfd,connfd; pid_t c ...

  6. OpenGL chapter5 基础纹理

    Chapter5 基础纹理 Contents: ==================================================== | 任务 | 使用的函数 ========== ...

  7. python cookbook第三版学习笔记九:函数

    接受任意数量参数的函数. 当传入函数的参数个数很多的时候,在函数定义的时候不需要为每一个参数定义一个变量,可以用*rest的方式来包含多余的参数. 如下面的代码,*rest包含了2,3,4这3个参数. ...

  8. Python 小而美的函数

    python提供了一些有趣且实用的函数,如any all zip,这些函数能够大幅简化我们得代码,可以更优雅的处理可迭代的对象,同时使用的时候也得注意一些情况   any any(iterable) ...

  9. 探究javascript对象和数组的异同,及函数变量缓存技巧

    javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...

随机推荐

  1. Zookeeper单机版安装(CentOS 7环境下)

    一.环境操作系统和软件版本介绍 1.环境操作系统为CentOS Linux release 7.2.1511 (Core) 可用cat /etc/redhat-release查询 2.软件版本 Zoo ...

  2. zabbix 布署实践【6 使用微信公众号-消息模版推送告警】

    使用这个服务的前提是,你必须要有一个微信订阅号,或者公众号,并且是通过认证的号 因为认证过后的号才有模版消息和获取用户openid等信息的权限 ,如下,登录微信公众号的登录页后,底下有个接口权限的展示 ...

  3. vb.net 结束进程

    Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click ...

  4. js实现多行文本超出一定字数显示省略号功能

    最近项目中遇到了一个关于超出一定字数用省略号显示的问题,其实这种形式很常见,公司简介.产品介绍啊里面都可能会用到,一行文字显示省略号很容易,多行就得想点办法了.在经过查阅.整理之后,我也算是实现了这个 ...

  5. DEBUG 调试

    1.Step Into (also F5) 跳入 2.Step Over (also F6) 跳过 3.Step Return (also F7) 执行完当前method,然后return跳出此met ...

  6. Elasticsearch相关配置(二)

    一.关于elasticsearch的基本概念 term 索引词,在elasticsearch中索引词(term)是一个能够被索引的精确值.foo,Foo Foo几个单词是不相同的索引词.索引词(ter ...

  7. ACM第四次积分赛

    虽然因为第一题给的数据有问题,没能四道题都做出来,但是这次第四名,进步很大,继续努力! SAU-ACM总比赛成绩       姓名     账号  上学期成绩 第一次成绩 第二次成绩 第三次成绩 第四 ...

  8. sharepoint 2013基于AD的Form表单登录(三)——选择用户时,屏蔽掉AD。

    //来源:http://www.cnblogs.com/lrforever/p/3695820.html 隐藏AD人员选择,$ad.IsVisible设置为true,则显示出AD里用户 $cpm = ...

  9. Java获取IP

    public static String getIpAddr(HttpServletRequest request) {        String ip = request.getHeader(&q ...

  10. CentOS7 citus9.5 集群安装及管理

    1 所有节点配置 #------服务安装 服务yum update -y #------扩展依赖安装yum install -y epel-release && yum update ...