在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. Linux下制作静(动)态库

    关键命令: 动态库制作命令 gcc xxx.c -fPIC -shared -o libxxx.so 静态库制作命令 gcc -c xxx.c ar crv libxxx.a xxx.o 例: //h ...

  2. 分布式缓存Memcached/memcached/memcache详解及区别

    先来解释下标题中的三种写法:首字母大写的Memcached,指的是Memcached服务器,就是独立运行Memcached的后台服务器,用于存储缓存数据的“容器”.memcached和memcache ...

  3. USACO 3.2 Factorials

    Factorials The factorial of an integer N, written N!, is the product of all the integers from 1 thro ...

  4. bootstrap的总结1 - 网格系统

    1.Bootstrap 网格系统 1)下表总结了 Bootstrap 网格系统如何跨多个设备工作: 2)Bootstrap 网格的基本结构 <div class="container& ...

  5. MVC 的 视图中 @section 是什么作用?

    可以定义一个渲染块,这个渲染块可以在LayoutPage里面引用,使用Html.RenderSection("section名称"); 可以指定一个bool参数指定如果Conten ...

  6. Gentoo解决Windows系统txt文本中文乱码问题

    Linux与Windows系统语言编码区别 在Linux操作系统下,我们有时打开在windows下的txt文件,发现在windows下能正常显示的txt文件出现了中文乱码. 出现这种情况的原因为两种操 ...

  7. 查看Oracle正在执行的任务

    select a.program, b.spid, c.sql_text,c.SQL_ID from v$session a, v$process b, v$sqlarea c where a.pad ...

  8. 使用ajax和history.pushState无刷新改变页面URL(转)

    表现 如果你使用chrome或者firefox等浏览器访问本博客.github.com.plus.google.com等网站时,细心的你会发现页面之间的点击是通过ajax异步请求的,同时页面的URL发 ...

  9. 解决Mac Linux USB Loader“Couldn't get security scoped bookmarks”错误

    Mac Linux USB Loader出现 "Couldn't get security scoped bookmarks"错误时, 需要删除 ~/Library/Contain ...

  10. 【实验室笔记】C#的Socket客户端接收和发送数据

    采用socket发送和接收数据的实验中,服务器采用的是网络助手作为模拟服务器端. 客户端程序流程: 应用的命名空间: using System.Net; using System.Net.Socket ...