Lua教程(二):基础知识、类型与值介绍

一、基础知识:

1. 第一个程序和函数:
    在目前这个学习阶段,运行Lua程序最好的方式就是通过Lua自带的解释器程序,如:
 

代码如下:

/> lua
    > print("Hello World")
    Hello World

这样我们就可以以交互性的方式输入lua代码,并立即得到执行结果了。对于代码块较少的测试程序来说,这种方式确实是非常方便的,然而对于相对复杂的程序而言,这种方式就不是很合适了。如果是这样,我们可以将Lua代码保存到一个独立的Lua程序文件中,之后再通过Lua解释器程序以命令行参数的形式执行文件中的Lua代码。如我们将下面的Lua代码保存到test.lua的文件中:

代码如下:

function fact(n)
    if n == 0 then
        return 1
    else
        return n * fact(n - 1)
    end
end
print("Enter a number:")
a = io.read("*number")
print(fact(a))

代码如下:

/> lua D:/test.lua
    Enter a number:
    4
    24

2. 代码规范:

1). Lua的多条语句之间并不要求任何分隔符,如C语言的分号(;),其中换行符也同样不能起到语句分隔的作用。因此下面的写法均是合法的。如:
 

代码如下:

a = 1
b = a * 2
   
a = 1;
b = a * 2;
   
a = 1; b = a * 2;
a = 1  b = a * 2

2). 通过dofile()方法引用其他Lua文件中的函数,如:

代码如下:

function fact(n)
    if n == 0 then
        return 1
    else
        return n * fact(n - 1)
    end
end

将上面的函数保存到test2.lua文件中。

代码如下:

/> lua
    > dofile("d:/test2.lua")
    > print(fact(4))
    24

3). 词法规范。
    和大多数其它语言一样,在声明变量时,变量名可以由任意字母、数字和下划线构成,但是不能以数字开头。在Lua中还有一个特殊的规则,即以下划线(_)开头,后面紧随多个大写字母(_VERSION),这些变量一般被Lua保留并用于特殊用途,因此我们在声明变量时需要尽量避免这样的声明方式,以免给后期的维护带来不必要的麻烦。
    Lua是大小写敏感的,因此对于一些Lua保留关键字的使用要特别小心,如and。但是And和AND则不是Lua的保留字。
    4). Lua中的注释分为两种,一种是单行注释,如:
 

代码如下:

--This is a single line comment.
    另外一种是多行注释,如:
    --[[
    This is a multi-lines comment.
    --]]

  3. 全局变量:
    在Lua中全局变量不需要声明,直接赋值即可。如果直接访问未初始化的全局变量,Lua也不会报错,直接返回nil。如果不想再使用该全局变量,可直接将其置为nil。如:
 

代码如下:

/> lua
    > print(b)
    nil
    > b = 10
    > print(b)
    10
    > b = nil
    > print(b)
    nil

  4. 解释器程序:
    命令行用法如下:
    lua [options] [lua-script [arguments] ]
    该工具的命令行选项主要有以下3个:
    -e: 可以直接执行命令行中Lua代码,如:lua -e "print(\"Hello World\")"
    -l: 加载该选项后的Lua库文件,如:lua -l mylib -e "x = 10",该命令在执行之前先将mylib中的Lua代码加载到内存中,在后面的命令中就可以直接使用该文件中定义的Lua函数了。
    -i: 在执行完指定的Lua程序文件之后,并不退出解释器程序,而是直接进入该程序的交互模式。   
    在解释器程序的交互模式下,我们可以通过在表达式前加等号(=)标识符的方式直接输出表达式的执行结果。通过该方式,我们可以将该程序用于计算器,如:
   

代码如下:

/> lua
    > = 3 + 1 + 4
    8

该小节最后需要介绍的是lua脚本的命令行参数访问规则。如:
 

代码如下:

/> lua lua-script.lua a b c

在该脚本的程序入口,lua解释器会将所有命令行参数创建一个名为arg的table。其中脚本名(lua-script.lua)位于table索引的0位置上。它的第一个参数(a)则位于索引1,其它的参数以此类推。这种索引方式和C语言中读取命令行参数的规则相同。但是不同的是,Lua提供了负数索引,用以访问脚本名称之前的命令行参数,如:
 

代码如下:

arg[-1] = lua
    arg[0] = lua-script.lua
    arg[1] = a
    arg[2] = b
    arg[3] = c

二、类型与值:

Lua是一种动态类型的语言。其语言本身没有提供类型定义的语法,每个值都“携带”了它自身的类型信息。在Lua中有8中基础类型,分别是:nil、boolean、number、string、userdata、function、thread和table。我们可以通过type函数获得变量的类型信息,该类型信息将以字符串的形式返回。如:
 

代码如下:

> print(type("hello world"))
    string
    > print(type(10.4))
    number
    > print(type(print))
    function
    > print(type(true))
    boolean
    > print(type(nil))
    nil
    > print(type(type(X)))
    string

  1. nil(空):
    nil是一种类型,它只有一个值nil,它的主要功能是由于区别其他任何值。就像之前所说的,一个全局变量在第一次赋值前的默认值的默认值就是nil,将nil赋予一个全局变量等同于删除它。Lua将nil用于表示一种“无效值”的情况。
    
    2. boolean(布尔):
    该类型有两个可选值:false和true。在Lua中只有当值是false和nil时才视为“假”,其它值均视为真,如数字零和空字符串,这一点和C语言是不同的。
    
    3. number(数字):
    Lua中的number用于表示实数。Lua中没有专门的类型表示整数。
    
    4. string(字符串):
    Lua中的字符串通常表示“一个字符序列”。字符串类型的变量是不可变的,因此不能像C语言中那样直接修改字符串的某一个字符,而是在修改的同时创建了新的字符串。如:

代码如下:

1 a = "one string"
2 b = string.gsub(a,"one","another")
3 print(a)
4 print(b)
    /> lua d:/test.lua   
    one string
    anotner string

Lua支持和C语言类似的字符转义序列,见下表:

在Lua中还可以通过[[ all strings ]]的方式来禁用[[ ]]中转义字符,如:
    page = [[ <html> <head> <title> An Html Page </title> </head> ]]
    如果两个方括号中包含这样的内容:a = b[c[i]],这样将会导致Lua的误解析,因此在这种情况下,我们可以将其改为[===[ 和 ]===]的形式,从而避免了误解析的发生。
    Lua提供了运行时的数字与字符串的自动转换。如:
 

代码如下:

> print("10" + 1)
    11
    > print("10 + 1")
    10 + 1

如果在实际编程中,不希望两个数字字符串被自动转换,而是实现字符串之间的连接,可以通过" .. "操作符来完成。如:
 

代码如下:

> print(10 .. 20)
    1020

注意..和两边的数字之间必须留有空格,否则就会被Lua误解析为小数点儿。
    尽管Lua提供了这种自动转换的功能,为了避免一些不可预测的行为发生,特别是因为Lua版本升级而导致的行为不一致现象。鉴于此,还是应该尽可能使用显示的转换,如字符串转数字的函数tonumber(),或者是数字转字符串的函数tostring()。对于前者,如果函数参数不能转换为数字,该函数返回nil。如:

代码如下:

line = io.read()
n = tonumber(line)
if n == nil then
    error(line .. " is not a valid number")
else
    print(n * 2)
end

关于Lua的字符串最后需要介绍的是"#"标识符,该标识符在字符串变量的前面将返回其后字符串的长度,如:

代码如下:

1 a = "hello"
2 print(#a)
    /> lua d:/test.lua
    5

    5. table(表):

我们可以将Lua中table类型视为“关联数组”,如C++标准库中的map,差别是Lua中table的键(key)可以为任意类型(nil除外),而map中的键只能为模参类型。此外,table没有固定的大小,可以动态的添加任意数量的元素到一个table中。table是Lua中最主要数据结构,其功能非常强大,可用于实现数组、集合、记录和队列数据结构。以下为table的变量声明,以及关联数据的初始化方式:

代码如下:

a = {}              -- 创建一个table对象,并将它的引用存储到a
k = "x"
a[k] = 10           -- 创建了新条目,key = "x", value = 10
a[20] = "great"     -- 新条目,key = 20, value = "great"
print(a["x"])
k = 20
print(a[k])         -- 打印great
a["x"] = a["x"] + 1
print(a["x"])       -- 打印11

所有的table都可以用不同类型的索引来访问value,当需要容纳新条目时,table会自动增长。

代码如下:

a = {}
for i = 1, 100 do
    a[i] = i * 2
end
print(a[9])
a["x"] = 10
print(a["x"])
print(a["y"])      --table中的变量和全局变量一样,没有赋值之前均为nil。

--输出结果为
--18
--10
--nil

在Lua中还提供了另外一种方法用于访问table中的值,见如下示例:

代码如下:

a.x = 10      --等同于a["x"] = 10
print(a.x)    --等同于print(a["x"])
print(a.y)    --等同于print(a["y"])

对于Lua来说,这两种方式是等价的。但是对于开发者而言,点的写法隐式的将table表示为记录,既C语言中的结构体。而之前讲述的字符串表示法则意味着任何字符串均可作为table的key。
    如果需要将table表示为传统的数组,只需将整数作为table的key即可。如:

代码如下:

a = {}
for i = 1,10 do
    a[i] = i * 2
end

for i = 1,10 do
    print(a[i])
end

在Lua中,我通常习惯以1作为数组索引的起始值。而且还有不少内部机制依赖于这个惯例。如:

代码如下:

a = {}
for i = 1,10 do
    a[i] = i * 2
end

for i = 1,#a do
    print(a[i])
end

由于数组实际上仍为一个table,所以对于数组大小的计算需要留意某些特殊的场景,如:

代码如下:

a = {}
    a[1000] = 1

在上面的示例中,数组a中索引值为1--999的元素的值均为nil。而Lua则将nil作为界定数据结尾的标志。当一个数组含有“空隙”时,即中间含有nil值,长度操作符#会认为这些nil元素就是结尾标志。当然这肯定不是我们想要的结果。因此对于这些含有“空隙”的数组,我们可以通过函数table.maxn()返回table的最大正数索引值。如:

代码如下:

a = {}
a[1000] = 1
print(table.maxn(a))   
 
-- 输出1000

    6. function(函数):
    在Lua中,函数可以存储在变量中,可以通过参数传递其它函数,还可以作为其它函数的返回值。这种特性使语言具有了极大的灵活性。

    7. userdata(自定义类型):
    由于userdata类型可以将任意C语言数据存储到Lua变量中。在Lua中,这种类型没有太多预定义的操作,只能进行赋值和相等性测试。userdata用于表示一种由应用程序或C语言库所创建的新类型。

(0)

相关推荐

  • C调用lua函数需要考虑的问题

    需要考虑的问题: 1. 使用 lua_pcall 可以调用 lua 函数,首先把 lua 函数入栈,然后把参数入栈, lua_pcall(luaState, 参数个数, 返回值个数, 0) : 2. 为了在事件触发时回调 lua 函数,需要把 lua 函数保存起来,使用 luaL_ref(luaState, LUA_REGISTRYINDEX) 将位于栈顶的 lua 函数保存到 LUA_REGISTRYINDEX 表里面, LUA_REGISTRYINDEX 是一个全局表, luaL_ref 将

  • Lua实现split函数

    function split(s, delim) if type(delim) ~= "string" or string.len(delim) <= 0 then return end local start = 1 local t = {} while true do local pos = string.find (s, delim, start, true) -- plain find if not pos then break end table.insert (t,

  • Lua中if语句嵌套的使用教程

    在Lua编程内嵌if-else语句,这意味着可以使用一个 if 或 else if 在另一个语句if或else if 语句中. 语法 if语句的嵌套语法如下: 复制代码 代码如下: if( boolean_expression 1) then    --[ Executes when the boolean expression 1 is true --]    if(boolean_expression 2)    then       --[ Executes when the boolea

  • Android基础知识及线性布局介绍

    目录 1.常见控件的基本属性 1.1控件的可见性 1.2控件的外边距 1.3控件的内边距 2.线性布局(Linear Layout) 2.1示例: 2.2微信界面实战 3.总结 1.常见控件的基本属性 android:id="@+id/button1":[设置控件id] android:layout_width[设置控件宽度]/android:layout_height[设置控件高度] wrap_content[控件的大小由内部决定] match_parent[控件的大小与父控件保持一

  • Lua教程(二):基础知识、类型与值介绍

    一.基础知识: 1. 第一个程序和函数:     在目前这个学习阶段,运行Lua程序最好的方式就是通过Lua自带的解释器程序,如:   复制代码 代码如下: /> lua     > print("Hello World")     Hello World 这样我们就可以以交互性的方式输入lua代码,并立即得到执行结果了.对于代码块较少的测试程序来说,这种方式确实是非常方便的,然而对于相对复杂的程序而言,这种方式就不是很合适了.如果是这样,我们可以将Lua代码保存到一个独立

  • Javascript & DHTML 实例编程(教程)基础知识

    [ 2007-04-11 14:31:50 | Author: never-online ] 这一节首先请下载JScript.chm这本手册,无论新手老手,有一本手册是免不了的,特别是对于新手,如果你没有空翻犀牛书,那么这本手册将是你了解这门语言的首选.下面所讲的大多数,手册上可以没有提及,或提及很少的内容. 以下的教程都是针对你对上面所说的JScript.chm这本手册了解的基础上而写的,如果你还没有看过JScript.chm,建议你先下载,边看手册,边看教程. JS的语法与大多数类C的语言差

  • C#基础知识之this关键字介绍

    一.this可以代表引用类的当前实例,包括继承而来的方法,通常可以省略. 复制代码 代码如下: public class Person {     public string Name { get; set; }     public int Age { get; set; }     public Person(string Name, int Age)     {         this.Age = Age;         this.Name = Name;     } } 这个不用多说

  • C#基础知识之new关键字介绍

    一.运算符 用于创建对象和调用构造函数.这种大家都比较熟悉,没什么好说的了. 二.修饰符 在用作修饰符时,new 关键字可以显式隐藏从基类继承的成员. 无new关键字代码: 有new关键字代码: 结果: 注意: 在子类中用 new 关键字修饰定义的与父类中同名的方法,叫覆盖.覆盖不会改变父类方法的功能. 当子类创建父类时,代码中A c = new B(),覆盖不会改变父类的功能.依然还是调用父类的功能. 三.new 约束 用于在泛型声明中约束可能用作类型参数的参数的类型. public clas

  • C#基础知识之base关键字介绍

    一.调用基类已被派生类重写的方法 复制代码 代码如下: public class Father {     public virtual void Say()     {         Console.WriteLine("Father Say");     } }   public class Son : Father {     public override void Say()     {         base.Say();         Console.WriteLi

  • MySQL 数据库的基础知识

    目录 1. 认识数据库 1.1 数据库和数据结构的关系 1.2 为什么需要数据库 1.3 数据库的存储 2. SQL 2.1 介绍 2.2 分类 3. 数据库的类别 3.1 关系型数据库 3.2 非关系型数据库 3.3 区别 4. MySQL 的程序结构 4.1 客户端和服务器 4.2 MySQL 的客户端-服务器结构 4.3 MySQL 服务器 1. 认识数据库 1.1 数据库和数据结构的关系 数据结构: 是指相互之间存在一种或多种特定关系的数据元素的集合,是一个抽象的学科 我们熟知的数据结构

  • .Net Core 3.1 Web API基础知识详解(收藏)

    目录 一.前言 二.Swagger调试Web API 三.配置文件 四.文件上传 五.统一WebApi数据返回格式 六.模型验证 七.日志使用 八.依赖注入 九.缓存 十.异常处理 十一.应用安全与JWT认证 十二.跨域 一.前言 随着近几年前后端分离.微服务等模式的兴起,.Net Core也似有如火如荼之势 ,自16年发布第一个版本到19年底的3.1 LTS版本,以及将发布的.NET 5,.NET Core一路更迭,在部署和开发工具上也都支持了跨平台应用.一直对.Net Core有所关注,但未

  • 3000字扫盲shell基础知识(新手必备)

    不论前端还是后端,再学会Linux的基础上,还应该学会写一些简单的Shell.不止是工作,日常中想做一些东西,如果手动做就很累人也费时间,如果你会用Shell脚本处理就很简单了,本文普及的shell的一些基础知识.后面还会持续更新. shell基础语法之变量 shell 变量命名 变量只能由大小写字母,数字和下划线组成. 变量名称不能以数字开头. 变量可以存储数字类型或者字符串类型. 变量赋值等号两边不能有空格. 字符串的变量可以用单引号或者双引号括起来. 定义变量: ➜ ~ name="xia

  • 前端组件化基础知识详细讲解

    目录 组件的基本概念 对象与组件的区别 组件 Component 特性 Attribute Attribute 对比 Property Attribute: Property: Class 属性 Style 属性 Href 属性 Input 和 value 如何设计组件状态 组件生命周期 Lifecycle Children 结束语 这里我们一起来学习前端组件化的知识,而组件化在前端架构里面是最重要的一个部分. 讲到前端架构,其实前端架构中最热门的就有两个话题,一个就是组件化,另一个就是架构模式

随机推荐