13

Lua函数 - DaemonCoder

 4 years ago
source link: https://www.daemoncoder.com/a/Lua%E5%87%BD%E6%95%B0/4d546b3d?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本篇介绍Lua函数的用法和相关细节。

函数的定义

函数定义的写法如下:
local function func_name(arg1, arg2, ...)
    -- statements
    return arg1, arg2, ...
end
local 指定函数的作用域为局部,不加local表示全局函数。function是关键字,表示声明的是一个函数,func_name自定义的函数名,函数名字可以省略,表示定义的是一个匿名函数(后面可以看到匿名函数的用法)。arg1, arg2为指定函数需要的参数,return指定函数返回。
下面看一个示例:
local function sum(a, b)
    return a + b
end
print(sum(2, 3))    -- 5

Lua函数支持多值返回:

local function get_msg() 
    return "Hello", true -- 返回多个值
end
local msg, err = get_msg() -- 接收返回的多个值
print(msg, err) -- Hello	true
在之前的文章 Lua 数据类型 中我们提到,Lua的函数和数值、字符串等一样,是一种类型,这就意味着我们可以创建一个值为函数的变量,先看一个示例:
local function sum(a, b)
    return a + b
end
print(sum(2, 3))    -- 5

local sub = function(a, b)
    return a - b
end
print(sub(8, 5)) -- 3

local math = {
    add = sum,
    sub = sub,
}
print(math.add(5, 3))   -- 8
print(math.sub(5, 3))   -- 2
通过个这个示例可以看到:
sum 是用之前介绍的常规方式定义的函数。
sub 是一个变量,值是一个匿名函数,则可以像函数调用的方式来调用 sub 变量。
math 是自定义的一个表,有两个字段 add、sub,这两个字段的值都是函数类型,所以也可以对其进行函数调用:math.add()、math.sub()。
Lua的函数,不要求传入的实参个数和形参个数一致。实参个数大于形参个数时,多传入的实参被忽略;实参个数小于形参个数时,不足的参数在函数体中的值为 nil 。
function say(a, b)
    print(a, b == nil)
end
say('www.daemoncoder.com')      -- www.daemoncoder.com  true
say('daemon', 'coder', '.com')  -- daemon   false
函数参数的值传递、引用传递情况,在和Lua变量的赋值表现是一样的。函数体内如果修改传入参数的值,是否会对调用函数处的值产生影响,不同的类型表现不一致,数值、字符串等不会产生影响,但是修改传入一个表内的字段值时会对原有表产生影响。具体示例可以参考之前的文章 Lua赋值
在C++中,函数参数可以设置默认值,当传入的实参数不足时,就是指定的默认值,写法如下:
#include <stdio.h>

void default_value(int a, int b = 2) {  // b参数设置默认值为2
    printf("a = %d, b = %d\n", a, b);
}
int main() {
    default_value(1);   // 输出: a = 1, b = 2
    return 0;
}
对不起,Lua不支持默认参数的写法!这种写法在Lua中会直接语法不通过。
熟悉Java的朋友,在很多情况下会用到方法重载,即定义两个名字相同的方法,传入的参数个数或类型不同,根据参数来决定具体是用的哪个方法,示例如下:
package test;

public class Test {

    public static void main(String[] args) {
        System.out.println(add(1, 2));                  // 3
        System.out.println(add("daemoncoder", ".com")); // daemoncoder.com
    }

    public static int add(int a, int b) {
        return a + b;
    }
    
    public static String add(String a, String b) {
        return a + b;
    }

}
如果你也希望Lua函数也支持这样的写法,那可能又要你失望了。对不起,Lua不支持函数重载!但是这种写法不会报错,看下面Lua示例:
function overload(a, b)
    print("In first function", a, b)
end
function overload(a, b, c)
    print("In second function", a, b, c == nil)
end
overload(1, 2) -- In second function	1	2	true
定义了两个都叫 overload 的方法,参数个数不同,来达到Java世界里的重载效果,最后调用处传入和第一个定义相匹配的参数,运行后发现其实调用的是第二个方法,参数c是nil。
Lua中定义两个相同名字的函数,没有重载一说,其实是函数的重定义,第二个函数会把第一个覆盖掉,第一个函数定义没有意义。

可变参数列表

Lua是支持可变参数列表的,在函数的最后一个参数定义处用 ... 表示,函数体中 ... 会当作一个表处理,示例如下:
function add(...)
    local s = 0
    local args = {...}
    for _, v in ipairs(args) do -- {...} 表示一个由所有变长参数构成的数组
        s = s + v
    end
    return s
end
print(add(1, 2, 3, 4)) -- 10

函数声明与调用顺序

函数需要先声明才能调用,先看示例:
function jump()
    run()
    print("jump")
end
-- jump(); -- 会报错:attempt to call a nil value (global 'run')
function run()
    print("run")
end
jump(); -- OK
第一处被注释的 jump() 处,不能调用 jump(),因为jump方法体中调用了 run(),此时还没有执行到 run 函数的定义处(即:run值为nil),会报错:attempt to call a nil value (global 'run')。
最后调用的 jump() 处,此时 run 函数已经定义过了,所以不会报错,也可以把 run() 放到 jump() 之前,这样就可以在被注释的地方正常调用了。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK