Lua支持面向对象,操作符为冒号‘:’。o:foo(x) <==> o.foo(o, x).

Lua程序可以调用C语言或者Lua实现的函数。Lua基础库中的所有函数都是用C实现的。但这些细节对于lua程序员是透明的。调用一个用C实现的函数,和调用一个用Lua实现的函数,二者没有任何区别。

函数的参数跟局部变量一样,用传入的实参来初始化,多余的实参被丢弃,多余的形参初始化为nil。

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

多返回值

不同于常规函数,Lua的函数可以返回多个返回值。一些Lua中预定义的函数可以返回多个返回值。例如string.find函数,在string中匹配一个sub-string,string.find返回sub-string的起始位置和结束位置。利用多赋值语句来获取函数的多个返回值。

s,e=string.find("hello Lua","Lua")
print(s,e)

7 9

function maximum(a)
local mi= --index
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({,,,,}))

ua会根据实际情况来使函数的返回值个数适应调用处的期望。

1)如果一个函数调用作为一条语句,所有的返回值都被丢弃

2)如果一个函数调用作为一个表达式的一部分时,返回值只保留第一个

3)只有当一个函数调用是一系列表达式中的最后一个元素(或仅有一个元素)时,才能获取它的所有返回值。这里的

“一系列表达式”在lua中表现为4中情况:

多重赋值,函数调用时传入的实参列表,,table的构造式,return语句中,。

在多重赋值中,若一个函数调用是最后的(或仅有的)一个表达式,那么Lua会保留尽可能多的返回值,用于
匹配赋值变量。

1.

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

x,y=foo2()
x=foo2() x="a",b被丢弃。
x,y,z=10,foo2()   ->x=10,y="a",z="b"

2.

如果一个函数调用不是一系列表达式的最后一个元素,那么将只产生一个值:

x,y=foo2(),"c"
print(x,y)  a,c

当一个函数调用作为另一个函数调用的最后一个(或仅有的)实参时,第一个函数的所有返回值都将作为实参传入第二个函数。
这样的例子已经见到很多了,如print。:

print(foo2()) --a b

print(foo2(),1) --a 1

3. table构造式可以完整接受一个函数调用的所有结果。

t={foo2()}
print(#t)

4.

最后一个情况是return语句,诸如return f()这样的语句,将返回f的所有返回值。

也可以将一个函数调用放入一对圆括号中,从而迫使它只返回以结果:

print((foo2())) --a

请主语return语句后面的内容是不需要圆括号的,如果写成
return (f(x))
将只返回一个值。

关于多重返回值还有介绍一个特殊函数--unpack,他接受一个数组作为参数,并从下标1开始返回该数组的所有元素:

print(unpack{10,20,30})
a,b=unpack{10,20,30} --a=10,b=20

An important use for unpack is in a generic call mechanism泛型调用. A generic call mechanism allows you to call any function, with any arguments, dynamically. In ANSI C, for instance, there is no way to do that. You can declare a function that receives a variable number of arguments (with stdarg.h) and you can call a variable function, using pointers to functions. However, you cannot call a function with a variable number of arguments: Each call you write in C has a fixed number of arguments and each argument has a fixed type. In Lua, if you want to call a variable function f with variable arguments in an array a, you simply write

    f(unpack(a))

The call to unpack returns all values in a, which become the arguments to f. For instance, if we execute

    f = string.find
a = {"hello", "ll"}

then the call f(unpack(a)) returns 3 and 4, exactly the same as the static call string.find("hello", "ll").

Although the predefined unpack is written in C, we could write it also in Lua, using recursion: 使用递归实现unpack。

    function unpack (t, i)
i = i or 1
if t[i] ~= nil then
return t[i], unpack(t, i + 1)
end
end

The first time we call it, with a single argument, i gets 1. Then the function returns t[1] followed by all results from unpack(t, 2), which in turn returns t[2] followed by all results from unpack(t, 3), and so on, until the last non-nil element.

变参

Lua中的一些函数接受可变数量的参数,例如print函数。print函数是用C来实现的,但是我们也可以用Lua来实现变参函数。下面是一个示例:

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

参数中的3个点(...)表示该函数可接受不同数量的实参,调用时3个点代表了实参。

表达式"..."的行为类似于一个具有多重返回值的函数,他返回的是当前函数的所有变长参数:
local a,b=...

上例用第一个和第二个变长参数来初始化这两个局部变量,实际上,还可以通过变长参数来模拟Lua中的
普通的参数传递机制。

如:
function foo(a,b,c)
等价于
function foo(...)
local a,b,c=...
end
对于那些喜爱Perl参数传递机制的人来说,可能会倾向于第二种形式。
如有这样一个函数:
function id(...)
return ...
end

他只是简单的返回所有市场。这是一个“多值恒定式(multi-value identity)"函数。
下面的这个函数行为非常类似于直接调用foo,但在调用foo()前先调用print:
function foo(...)
print("calling foo:",...)
return foo(...)
end
这种技巧对于跟踪某个特定函数调用很有帮助。

具有参数named argu

(lua函数调用特殊语法,当实参只有一个table时,可以省略圆括号)。

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

indow函数可以检查必须的参数,并且给可选参数赋予默认值等。假设_Window函数可以用来创建一个新窗口,但是它必须要全部的参数。那我们就可以重新定义一个Window函数如下:

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.x or , -- default value
options.y or , -- default value
options.width, options.height,
options.background or "white", -- default
options.border -- default is false (nil)
)
end

二、深入函数:

在Lua中有一个容易混淆的概念是,函数与所有其他值一样都是匿名的,即他们都没有名称。当讨论一个函数名时(例如print)。实际上是在讨论持有某函数的变量,这与其他变量持有各种值一个道理,下面这个实例足以说明

a = { p = print }
    a.p("Hello World")
    b = print
    b("Hello World")

function foo(x) return 2 * x end

这只是一种所谓的”语法糖“而已,实际等价于:

foo = function(x) return 2 * x end

因此,一个函数定义实际上就是一条语句(更准备说是赋值语句)。这条语句创建了一种类型为”函数“的值。可以将表达式"function(x)<body> end"视为一种函数的构造式,就像table的构造式{}一样。将这种函数构造式的结果称为一个”匿名函数”。

下面的示例显示了匿名函数的方便性,它的使用方式有些类似于Java中的匿名类,如:
    table.sort(test_table,function(a,b) return (a.name > b.name) end)

像sort这样的函数,接受另一个参数作为实参的,称其是一个”高阶函数“,高阶函数是一种 强大的编程机制,应用匿名函数来创建高阶函数所需的实参则可以带来更大的灵活性。但请记住,高阶函数并没有什么特权。Lua强调将函数视为”first-class valeu",所以,高阶函数只是基于该观点的应用体现而已

1. closure(闭合函数):闭包
    若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,见如下示例:

function newCounter()
local i =
return function() --匿名函数
i = i +
return i
end
end
c1 = newCounter()
print("The return value of first call is " .. c1())
print("The return value of second call is " .. c1())
--输出结果为:
--The return value of first call is 1
--The return value of second call is 2

简单来讲,一个closure就是一个函数加上该函数所需访问的所有“非局部变量”

在上面的示例中,我们将newCounter()函数称为闭包函数。其函数体内的局部变量i被称为"非局部变量",和普通局部变量不同的是该变量被newCounter函数体内的匿名函数访问并操作。再有就是在函数newCounter返回后,其值仍然被保留并可用于下一次计算。再看一下下面的调用方式。

在上面的示例中,我们将newCounter()函数称为闭包函数。其函数体内的局部变量i被称为"非局部变量",和普通局部变量不同的是该变量被newCounter函数体内的匿名函数访问并操作。再有就是在函数newCounter返回后,其值仍然被保留并可用于下一次计算。再看一下下面的调用方式。

function newCounter()
local i =
return function() --匿名函数
i = i +
return i
end
end
c1 = newCounter()
c2 = newCounter()
print("The return value of first call with c1 is " .. c1())
print("The return value of first call with c2 is " .. c2())
print("The return value of second call with c1 is " .. c1())
--输出结果为:
--The return value of first call with c1 is 1
--The return value of first call with c2 is 1
--The return value of second call with c1 is 2

由此可以推出,Lua每次在给新的闭包变量赋值时,都会让不同的闭包变量拥有独立的"非局部变量"。下面的示例将给出基于闭包的更为通用性的用法:

do
--这里将原有的文件打开函数赋值给"私有变量"oldOpen,该变量在块外无法访问。
local oldOpen = io.open
--新增一个匿名函数,用于判断本次文件打开操作的合法性。
local access_OK = function(filename,mode) <检查访问权限> end
--将原有的io.open函数变量指向新的函数,同时在新函数中调用老函数以完成真正的打开操作。
io.open = function(filename,mode)
if access_OK(filename,mode) then
return oldOpen(filename,mode)
else
return nil,"Access denied"
end
end
end

这个示例的精彩之处在于,经过重新定义后,一个程序就只能通过新的受限版本来调用原来那个未受限的open函数了。示例将原来不安全的版本保存到closure的一个私有变量中。从而使得外部再也无法直接访问原来的版本了。

2. 非全局函数:
    从上一小节中可以看出,Lua中的函数不仅可以直接赋值给全局变量,同时也可以赋值给其他类型的变量,如局部变量和table中的字段等。事实上,Lua库中大多数table都带有函数,如io.read、math.sin等。这种写法有些类似于C++中的结构体。如:
    Lib = {}
    Lib.add = function(x,y) return x + y end
    Lib.sub = function(x,y) return x - y end
    或者是在table的构造式中直接初始化,如:
    Lib = { add = function(x,y) return x + y end, 
               sub = function(x,y) return x - y end
             }
    除此之外,Lua还提供另外一种语法来定义此类函数,如:
    Lib = {}
    function Lib.add(x,y) return x + y end
    function Lib.sub(x,y) return x - y end
    对于Lua中的局部函数,其语义在理解上也是非常简单的。由于Lua中都是以程序块作为执行单元,因此程序块内的局部函数在程序块外是无法访问的,如:

 do
2 local f = function(x,y) return x + y end
3 --do something with f.
4 f(4,5)
5 end

对于这种局部函数,Lua还提供另外一种更为简洁的定义方式,如:
    local function f(x,y) return x + y end
    该写法等价于:
    local f
    f = function(x,y) return x + y end

3. 正确的尾调用:
    在Lua中支持这样一种函数调用的优化,即“尾调用消除”。我们可以将这种函数调用方式视为goto语句,如:
    function f(x) return g(x) end
    由于g(x)函数是f(x)函数的最后一条语句,在函数g返回之后,f()函数将没有任何指令需要被执行,因此在函数g()返回时,可以直接返回到f()函数的调用点。由此可见,Lua解释器一旦发现g()函数是f()函数的尾调用,那么在调用g()时将不会产生因函数调用而引起的栈开销。这里需要强调的是,尾调用函数一定是其调用函数的最后一条语句,否则Lua不会进行优化。然而事实上,我们在很多看似是尾调用的场景中,实际上并不是真正的尾调用,如:
    function f(x) g(x) end            --没有return语句的明确提示
    function f(x) return g(x) + 1  --在g()函数返回之后仍需执行一次加一的指令。
    function f(x) return x or g(x) --如果g()函数返回多个值,该操作会强制要求g()函数只返回一个值。
    function f(x) return (g(x))     --原因同上。
    在Lua中,只有"return <func>(<args>)"形式才是标准的尾调用,至于参数中(args)是否包含表达式,由于表达式的执行是在函数调用之前完成的,因此不会影响该函数成为尾调用函数。

在之前提到了,一条“尾调用”就好比是一条“goto语句”。因此在Lua中”尾调用“的一大应用就是编写”状态机(state machine)"。这种程序通常以一个函数来表示一个的状态,改变状态就是goto到另一个特定的函数。举一个简单的迷宫游戏的例子来说明这个问题。例如,一个迷宫有几间房间,每间房间中最多有东南西北4扇门。用户在每一步移动中都需要输入一个移动的方向。如果在某个方向上有门,那么用户可以进入相应的房间;不然,程序就打印一条警告。游戏目标是让用户从最初的房间走到最终的房间。

这个游戏就是一种典型的状态机,其中当前房间是一种状态。可以将迷宫中的每间房间实现为一个函数,并使用”尾调用“来实现从一间房间移动到另一间房间。在以下代码中,实现一个具有4间房间的迷宫:

    function room1 ()
local move = io.read()
if move == "south" then return room3()
elseif move == "east" then return room2()
else print("invalid move")
return room1() -- stay in the same room
end
end function room2 ()
local move = io.read()
if move == "south" then return room4()
elseif move == "west" then return room1()
else print("invalid move")
return room2()
end
end function room3 ()
local move = io.read()
if move == "north" then return room1()
elseif move == "east" then return room4()
else print("invalid move")
return room3()
end
end function room4 ()
print("congratulations!")
end

通过调用初始房间来开始这个游戏:
room1()
若没有”尾调用消除“的话,每次用户的移动都会创建一个新的stack,移动若干步之后就有可能导致栈溢出。而”尾调用消除“则对用户移动的次数没有任何限制。这是因为每次移动实际上都只是完成一条goto语句到另一个函数,而非传统的函数调用。

对于这个简单的游戏而言,或许会觉得将程序设计为数据驱动的会更好一点,其中将房间和移动记录在一些table中。不过,如果游戏中的每间房间都有各自特殊情况的话,采用这种状态机的设计则更为合适。

转自;programming in lua.

Lua function 函数的更多相关文章

  1. lua闭合函数

    function count( ... ) return function( ... ) i = i+ return i end end local func = count(...) print(f ...

  2. Lua基础 函数(一)

    转自: http://blog.csdn.net/wzzfeitian/article/details/8653101 在Lua中,函数是对语句和表达式进行抽象的主要方法.既可以用来处理一些特殊的工作 ...

  3. lua c函数注册器

    lua与c的交互 关于lua和c的交互,主要有两个方面,一是lua调用c的函数,而另一个则是c调用lua函数.而这些都是通过lua stack来进行的. c调用lua 在c里面使用lua,主要是通过l ...

  4. lua基础---函数

    Lua的函数功能很强大,保留了C语言的一些基本的特性,但是也有C语言没有的特性,比如,lua可以在一个函数返回多个值,我们来看看下面这个案例: 解释运行: lua test5.lua --定义一个函数 ...

  5. 【lua实战摸索】在b.lua调用a.lua的函数

    需要掌握知识: lua table的使用(创建自己函数的表作为函数库) 普通函数的调用:tab.func(tab,参数) 等效于表中函数的调用tab:func(参数) 基本思路: 1.在相同目录下创建 ...

  6. lua的函数初识

    学习到Lua的函数.认为有必要记下来. 參考教程:Programming in Lua 函数能够以表达式或陈述语句出现,例如以下所看到的: print(8*9, 9/8) a = math.sin(3 ...

  7. Lua的函数的定义、math数学函数、lua字符串的处理、lua支持的字符串类、模式串中的特殊字符_学习笔记04

    Lua的函数的定义.math数学函数 定义函数 function [function name] (param1,param2) [function code] --定义一个函数用来求的两个数字的和 ...

  8. Lua 常用函数 一

    lua_getallocf lua_Alloc lua_getallocf (lua_State *L, void **ud); 返回给定状态机的内存分配器函数.如果 ud 不是 NULL ,Lua ...

  9. 关于Function()函数对象的那些小九九

    概念:首先,函数是一种特殊类型的数据,函数也是数据类型的一种,实际上函数也是一种对象,函数对象的内建构造器是Function(); 函数的几种创建方式: 函数声明法: function sum(a,b ...

随机推荐

  1. Android ListView实现仿iPhone实现左滑删除按钮

    需要自定义ListView.这里就交FloatDelListView吧. 复写onTouchEvent方法.如下: @Override public boolean onTouchEvent(Moti ...

  2. 调试php的soapCient

    try { import('@.Ext.xml'); header("Content-Type:text/html; charset=utf-8"); $soap = new So ...

  3. HTML5本地化应用开发-HTML5 Web存储详解

    文章不是简单的的Ctrl C与V,而是一个字一个标点符号慢慢写出来的.我认为这才是是对读者的负责,本教程由技术爱好者成笑笑(博客:http://www.chengxiaoxiao.com/)写作完成. ...

  4. php 这门语言

    1,基本语法 php在解析一个文件时,会查找开始和结束标记,在开始标记和结束标记之外的会被php引擎忽略 注释:使用 // 和 /*这里是注释*/ 2,php 数据类型 整形 (2345) 浮点型(3 ...

  5. 尽量不要用select into 复制表

    select into 复制表会带来灾难后果,因为只是复制了一个外壳,就像克隆人,有躯体没意识,像原表的主键 外键 约束 触发器 索引都不会被复制过来, 创建一个表:CREATE TABLE [dbo ...

  6. CI 笔记,使用 json的参考文档(废弃)

    Json的处理转换, Json转换步骤, a)         先设置json为空字符串, b)         While循环,遍历, While(!!$row = mysql_fetch_arra ...

  7. iOS Core Animation学习总结(1)--CALayer常用属性

    图层是core animation的基础, UIView之所以能显示在屏幕上,靠的是其内部的这个图层,即每个UIView 都有 CALayer,可通过UIView.layer或者[UIView lay ...

  8. Android学习2--项目文件列表简单分析

    使用Eclipse创建的默认项目文件列表如下: src:src目录是Android工程的源程序目录,该目录用于存放Java项目的源代码 gen:gen目录存放所有自动生成的文件,在这个目录中最关键的文 ...

  9. Ubuntu 源

    原文地址: Ubuntu 12.04添加源 sudo vim /etc/apt/sources.list #网易163 deb http://mirrors.163.com/ubuntu/ preci ...

  10. 自定义流程gooflow.08 demo在线演示

    一.功能简介 gooflow功能清单1.自定义流程绘制2.自定义属性添加3.支持3种步骤类型 普通审批步骤 自动决策步骤 手动决策步骤 4.决策方式(支持js决策,sql语句决策) 5.审批人员参与方 ...