Lua极简入门指南(一):函数篇

Lua 和其他很多语言一样,函数调用时参数列表被包裹在括号中:

代码如下:

print('Hello World')

特别的情况是,如果函数调用时只有一个参数,并且此参数为字符串 literal(字面量)或者 table 构造器(constructor)时,包裹参数的括号可以省略:

代码如下:

print 'Hello World' <--> print('Hello World')
type{}              <--> type({})

Lua 为面向对象的调用提供了特殊的语法:

代码如下:

o:foo(x) <--> o.foo(o, x)

Lua 调用的函数可能被定义在 Lua 中,也可能被定义在 C 中(Lua 标准库中的所有函数都使用 C 编写)。

函数的定义

代码如下:

function add(a)
    local sum = 0
    for i = 1, #a do
        sum = sum + a[i]
    end
    return sum
end
 
print(add{1, 2, 3})

函数调用时,实参(arguments)和形参(parameters)个数可以不匹配,多余的实参会被丢弃,多余的形参值为 nil,例如:

代码如下:

function f(a, b) print(a, b) end
f(3)         --> 3 nil
f(3, 4)      --> 3 4
f(3, 4, 5)   --> 3 4

函数多值返回

在 Lua 中函数可以返回多个值。例如:

代码如下:

function maximum(a)
    local mi = 1
    local m = a[mi]
    for i = 1, #a do
        if a[i] > m then
            mi = i; m = a[i]
        end
    end
    return m, mi
end
 
print(maximum{8, 10, 23, 12, 5})

在多赋值时,多余的值会被丢弃,不足时变量值为 nil:

代码如下:

x, y = 1        --> x == 1, y == nil
x, y = 1, 2, 3  --> x == 1, y == 2

在函数调用时形参的值处理上,在函数返回值的获取上,都遵循这个规则。例如:

代码如下:

function foo0() end
function foo1() return 'a' end
function foo2() return 'a', 'b' end
 
x, y = foo2()         --> x == 'a', y == 'b'
x = foo2()            --> x == 'a'
x, y, z = 10, foo2()  --> x == 10, y == 'a', z == 'b'
 
t = { foo2() }        --> {'a', 'b'}

再看一个例子:

代码如下:

function foo2() return 'a', 'b' end
x, y = foo2(), 20    --> x == 'a', y == 20

这里,由于函数调用不是在列表的最后一个位置,这时候函数只提供一个值。一个更有意义的例子:

代码如下:

function foo2() return 'a', 'b' end
 
print(foo2())     --> a b
print(foo2(), 1)  --> a 1

如果函数调用在列表的最后一个位置,同时使用 () 包裹函数调用,这时候函数也只提供一个值:

代码如下:

function foo2() return 'a', 'b' end
 
print(foo2())     --> a b
print((foo2()))   --> a

一个比较有用的利用函数多值返回的特性的函数是 table.unpack,它接受一个数组作为参数,返回数组中的所有元素:

代码如下:

print({10, 20, 30})               --> table: 00000000005BBE00
print(table.unpack{10, 20, 30})   --> 10 20 30

变长参数

我们在使用 print 函数的时候可以传递任意数目的参数。Lua 提供了 … 表示参数列表,让我们实现类似 print 的函数:

代码如下:

function add(...)
    local s = 0
    for _, v in ipairs{...} do
        s = s + v
    end
    return s
end
 
print(add(3, 4, 5))  --> 12

再一个例子:

代码如下:

function test(...)
    local a, b = ...
    print(a, b)
end
 
test(1, 2, 3)  --> 1 2

还有一个特殊情况,我们需要注意:

代码如下:

function p(...)
    for _, v in ipairs{...} do
        print(v)
    end
end
 
p(1, nil, 3)      --> 1
print(1, nil, 3)  --> 1 nil 3

上例可以看到,我们的 p 函数在参数中存在 nil 时并非按我们的意愿输出了结果。Lua 提供了一个 table.pack 函数,用于获取其调用参数(包括 nil 参数)并返回一个包含所有参数的 table,此 table 存在一个额外的域 n,用于表示参数的数量:

代码如下:

function p(...)
    local arg = table.pack(...)
    for i = 1, arg.n do
        print(arg[i])
    end
end
 
p(1, nil, 3, nil)  --> 1 nil 3 nil

不过需要注意的是,{…} 相比 table.pack(…) 来说更加高效,我们可以在确保没有 nil 参数的时候使用。

函数是第一类值(first-class values)

我们能够像使用其他变量一样的使用函数:

代码如下:

a = { p = print }
a.p('Hello World')  --> Hello World
print = math.sin
a.p(print(1))       --> 0.8414709848079

类似于 {} 作为 table 的构造器,我们可以认为 function(x) end 为函数的构造器:

代码如下:

local add = function(a, b)
    return a + b
end
 
print(add(1, 2))
 
-- 另一种写法
local function add(a, b)
    return a + b
end

table.sort 函数用于排序,它可以接受一个排序函数作为参数:

代码如下:

network = {
    { name = "grauna", IP = "210.26.30.34" },
    { name = "arraial", IP = "210.26.30.23" },
    { name = "lua", IP = "210.26.23.12" },
    { name = "derain", IP = "210.26.23.20" },
}
 
print('--------------')
for _, v in ipairs(network) do
    print(v.name)
end
 
table.sort(network, function(a, b)
    return a.name > b.name
end)
 
print('--------------')
for _, v in ipairs(network) do
    print(v.name)
end

输出结果为:

代码如下:

--------------
grauna
arraial
lua
derain
--------------
lua
grauna
derain
arraial

在此例中,我们提供的排序函数作为一个参数传递给 table.sort 函数,像此排序函数这样没有名字的函数被叫做匿名函数。

闭包(closures)

很多语言都支持闭包(Golang、JavaScript 等)。一个函数和其访问的外部变量组成一个闭包。看一个例子:

代码如下:

function newCounter()
    local i = 0
    return function()
        i = i + 1
        return i
    end
end
 
c1 = newCounter()
print(c1())   --> 1
print(c1())   --> 2

这里的 c1 就是一个闭包(外部变量为 i),每次调用 newCounter 都会创建一个闭包(并创建一个新的变量 i):

代码如下:

c2 = newCounter()
print(c2())   --> 1
print(c1())   --> 3
print(c2())   --> 2

(0)

相关推荐

  • 使用lua实现php的print_r()函数功能

    之前写了一些类似php的函数,下面再来一个print_r()函数,代码如下: 复制代码 代码如下: function pr (t, name, indent)       local tableList = {}       function table_r (t, name, indent, full)           local id = not full and name or type(name)~="number" and tostring(name) or '['..n

  • Lua中的一些常用函数库实例讲解

    前言 这篇文章将会来一些比较轻松的内容,就是简单的介绍一下Lua中几个常用的库.简单的说就是几个API的介绍.所以说,看起来比较容易,也没有多大的分量.就是纯粹的总结.使用库就是为了方便我们的开发,提高开发效率,同时也能保证代码的质量.希望大家以后也不要重复造轮子了. 数学库 数学库(math)由一组标准的数学函数构成.这里主要介绍几个常用的函数,其它的大家可以自行百度解决. 三角函数(sin,cos,tan--) 所有的三角函数都使用弧度单位,可以用函数deg(角度)和rad(弧度)来转换角度

  • Lua中的模块与module函数详解

    很快就要开始介绍Lua里的"面向对象"了,在此之前,我们先来了解一下Lua的模块. 1.编写一个简单的模块 Lua的模块是什么东西呢?通常我们可以理解为是一个table,这个table里有一些变量.一些函数- 等等,这不就是我们所熟悉的类吗? 没错,和类很像(实际上我说不出它们的区别).   我们来看看一个简单的模块,新建一个文件,命名为game.lua,代码如下: 复制代码 代码如下: game = {} function game.play()     print("那么

  • Lua函数与字符串处理简明总结

    函数的定义是以function关键字开始的,后面函数的名称,然后是要传递给函数的参数,如果没有参数传给函数,仍然需要用()来表示一个空的参数列表,以end关键字结尾. 复制代码 代码如下: function 函数名()  ...  ...  ... end 1. 单一参数 复制代码 代码如下: function F_1(var)  print("My website is: "  var) end 参数var传递给了函数,并在函数中使用,同时,函数中的参数是局部变量,在函数调用结束后被

  • Lua字符串库中的几个重点函数介绍

    在<Lua中的一些库>中也说到了,要对string库的模式匹配进行单独的讲解.对于字符串的处理,对于任何语言的学习来说,都是一个难点,而且也是一个必会的知识点.给你一个字符串,让你按照某种需求进行处理,你不会,那是多么尴尬的一件事情.所以,看完<Lua中的一些库>和这篇文章之后,我争取做到让你在处理字符串时,不再感到捉襟见肘,不再尴尬. 说到Lua中的模式匹配,基本上就是围绕着以下几个函数展开的: 1.find: 2.match: 3.gsub: 4.gmatch. 我的总结也就是

  • Lua中调用函数使用点号和冒号的区别

    本文是面向对象预热篇,讲解函数两种调用方式的区别,初学者比较容易被坑. 1.初学者最易混乱Top1--调用函数时用点号还是用冒号? 我们来看看下面的两句代码: 复制代码 代码如下: mSprite.setPosition(100, 20); mSprite:setPosition(100, 20); 对于初次接触Lua的朋友来说,这简直就是噩梦,为嘛函数的调用有两种形式,是让我们随便挑的意思吗?   这两种形式是有区别的,区别很大,但只有一个. 不过,暂时不解释,后面再介绍. 2.最简单的类 我

  • Lua中实现php的strpos()以及strrpos()函数

    在来写一个lua中实现php的strpos()函数,查找某个字符串在指定字符串首次出现的位置,其实lua中也为我们提供了这样的函数使用string.find()即可获得,下面我们还是简单写一个函数,代码如下: 复制代码 代码如下: function strpos (str, f)       if str ~= nil and f ~= nil then           return (string.find(str, f))       else          return nil  

  • Lua极简入门指南(一):函数篇

    Lua 和其他很多语言一样,函数调用时参数列表被包裹在括号中: 复制代码 代码如下: print('Hello World') 特别的情况是,如果函数调用时只有一个参数,并且此参数为字符串 literal(字面量)或者 table 构造器(constructor)时,包裹参数的括号可以省略: 复制代码 代码如下: print 'Hello World' <--> print('Hello World') type{}              <--> type({}) Lua 为

  • Lua极简入门指南(一):基础知识篇

    本文是<Programming in Lua 3rd>读书笔记. Chunks 一个 Chunk 就是一组被执行的语句,例如一个文件或者交互模式下的一行. 标识符(identifiers) 我们应该避免使用以 _ 开头并跟上一个或者多个大写字母的字符串来作标识符,它们被保留作特殊的用途(例如:_VERSION). 注释 单行注释使用 复制代码 代码如下: -- 多行注释使用 复制代码 代码如下: --[[ 和 --]] 类型简介 Lua 存在的数据类型包括: 1.nil.此类型只有一个值 ni

  • Lua极简入门指南:全局变量

    全局环境 Lua 把全局变量放在一个 table _G 中,这个 table 被叫做全局环境(global environment).打印所有的全局变量名: 复制代码 代码如下: for n in pairs(_G) do print(n) end _ENV(Lua 5.2 开始支持) 对于一个 free name(名字没有绑定任何声明)var 实际上会被转换为 _ENV.var(每个 chunk 中都会存在一个名为 _ENV 的变量): 复制代码 代码如下: v1 = 1 local v2 =

  • Lua极简入门指南(三): loadfile和错误处理

    编译 Lua 虽然是解释性语言,但 Lua 源码总是被编译为中间形式后再执行. dofile 用于载入并执行一个 Lua 文件,相比之下,loadfile 用于载入一个 Lua 文件,但并不执行,确切的说 loadfile 编译了一个 chunk,并返回此被编译的 chunk(被作为一个函数): 复制代码 代码如下: c = loadfile('./test.lua') c() dofile 可以被实现为: 复制代码 代码如下: function dofile(filename)     loc

  • Lua极简入门指南(六):模块

    从用户的角度来看,一个模块能够通过 require 加载并返回一个 table,模块导出的接口都被定义在此 table 中(此 table 被作为一个 namespace).所有的标准库都是模块.标准库被预先加载了,就像这样: 复制代码 代码如下: math = require 'math' string = require 'string' require 函数 使用 require 函数加载模块能够避免多次重复加载模块.加载一个模块: 复制代码 代码如下: require 'modulena

  • Lua 极简入门指南(七):面向对象编程

    类 在很多面向对象的语言中有类(class)的概念,对象是类的实例.Lua 中不存在类的概念.Lua 就像 JavaScript 一样是面向原型的语言(http://en.wikipedia.org/wiki/Prototype-based_programming),这类语言使用一个对象表示一个"类",其他对象(此类的实例)使用此对象作为原型.我们有两个 table p 和 obj,将 p 设置为 obj 的原型(回顾:http://www.jb51.net/article/56690

  • Pytorch学习笔记DCGAN极简入门教程

    目录 1.图片分类网络 2.图片生成网络 首先是图片分类网络: 重点是生成网络 每一个step分为三个步骤: 1.图片分类网络 这是一个二分类网络,可以是alxnet ,vgg,resnet任何一个,负责对图片进行二分类,区分图片是真实图片还是生成的图片 2.图片生成网络 输入是一个随机噪声,输出是一张图片,使用的是反卷积层 相信学过深度学习的都能写出这两个网络,当然如果你写不出来,没关系,有人替你写好了 首先是图片分类网络: 简单来说就是cnn+relu+sogmid,可以换成任何一个分类网络

  • JavaScript极简入门教程(一):基础篇

    阅读本文需要有其他语言的编程经验. 开始学习之前 大多数的编程语言都存在好的部分和差的部分.本文只讲述 JavaScript 中好的部分,这是因为: 1.仅仅学习好的部分能够缩短学习时间 2.编写的代码更加健壮 3.编写的代码更加易读 4.编写的代码更加易于维护 弱类型和强类型 通常来说,越早的修复错误,为之付出的代价就越小.强类型语言的编译器可以在编译时检查某些错误.而 JavaScript 是一门弱类型语言,其解释器无法检查类型错误,但实践表明: 1.强类型能够避免的错误并不是那些关键性错误

  • JavaScript极简入门教程(二):对象和函数

    阅读本文需要有其他语言的编程经验. JavaScript 中的简单类型包括: 1.数字 2.字符串 3.布尔(true 和 false) 4.null 5.undefined 此外的其他类型均是对象(我们不要被 typeof 操作符的返回值所迷惑),例如: 1.函数 2.数组 3.正则表达式 4.对象(对象自然也是对象) 对象基础 在 JavaScript 中,对象是属性的集合(对象为关联数组),每个属性包括: 1.属性名,必须为字符串 2.属性值,可以为除了 undefined 之外的任何值

  • Python编程入门指南之函数

    目录 Python编程:函数 定义和调用函数 向函数传递信息 传递实参:位置实参 传递实参:关键字实参 传递实参:默认值 传递列表 禁止函数修改列表 传递任意数量实参 返回值 返回简单值 让实参可选 返回字典 将函数存储在模块中 导入整个模块 导入特定函数 使用as给函数指定别名 使用as给模块指定别名 导入模块中的所有函数 函数编写指南 总结 Python编程:函数 函数是带名字的代码块,用于完成具体的工作.要执行函数定义的特定任务,可调用该函数.需要在程序中多次执行同一项任务时,你无需反复编

随机推荐