Lua学习笔记

一. Lua简介

        Lua的底层是C实现的,高效、灵活、小巧。

二. 参考资料:

        Lua && C# (bilibili.com)

        基础语法 - LuatOS 文档

        Lua 5.3 参考手册 - 目录

        前两个参考资料看完基本上就能读代码、写代码了,第三个参考资料是官方文档。

二. 与C++对比

1.变量默认作用域

        与C++不同,Lua声明的变量默认为全局变量。为了减少不必要的内存占用,应该尽量使用local

2.变量默认值

        Lua中变量默认值为nil(表明该值为无效值),一般情况下删除一个变量就是给它赋nil值,然后等垃圾回收机制自己释放。另外,在Lua中地址(函数地址或表地址)也被当作一个变量看待。

a = 123 -- 声明一个number变量a
a = nil -- 删除a(其实就是重置a的值)

3.示例代码——求a, b中的最大值(a, b为整数)

-- Lua代码
function Max(a,b)
    return a > b and a or b
end
// C++代码
int Max(int a, int b)
{
	return a > b ? a : b;
}

4.示例代码——交换a, b的值

-- Lua代码
a,b = b,a
// C++代码
template
void Swap(T &a, T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

5.示例代码——快排

-- Lua代码
function Partition(nums, left, right)
    key = nums[left]

    while left < right do
        while left < right and key <= nums[right] do
            right = right - 1
        end
        nums[left] = nums[right]

        while left < right and key >= nums[left] do
            left = left + 1
        end
        nums[right] = nums[left]
    end

    nums[left] = key
    return left
end

function QSort(nums, left, right)
    if left >= right then 
        return
    end

    keyPos = Partition(nums, left, right)
    QSort(nums, left, keyPos - 1)
    QSort(nums, keyPos + 1, right)
end

nums = { 4,2,5,1,3 }
QSort(nums, 1, #nums)
print(table.concat(nums, " "))
// C++代码
#include 
#include 

int Partition(std::vector &nums, int left, int right)
{
    int key = nums[left];

    while (left < right)
    {
        while (left < right && nums[right] >= key)
            --right;
        nums[left] = nums[right];
        while (left < right && nums[left] <= key)
            ++left;
        nums[right] = nums[left];
    }

    nums[left] = key;
    return left;
}
void QSort(std::vector &nums, int left, int right)
{
    if (left >= right) return;

    int KeyPos = Partition(nums, left, right);
    QSort(nums, left, KeyPos - 1);
    QSort(nums, KeyPos + 1, right);
}

int main() 
{
    std::vector nums = { 4,2,5,1,3 };
    QSort(nums, 0, nums.size() - 1);
    
    for (int n : nums)
        std::cout << n << ' ';
}

5.循环

        for循环、while循环与C++语法类似。

        需要注意的是,Luarepeat...untilC++中的do...while语句有所不同,前者执行循环语句块的条件是判断语句为false,而后者执行语句块的条件是判断语句为true

        另外,Lua中无continue关键字。

6.表(table)

        底层是数组+哈希表实现的。

        Lua里面table可以用正整数(从1开始计数)或字符串作为下标,未指明时默认使用正整数作为下标(有点像C++enum类型)。
        为了变量含义更加明确,一般情况下使用字符串作为下标。
        _G为全局表存放所有全局变量。

7.部分操作符与关键字的比较

Lua C++
不等于 ~=

!=

阶乘 ^ powf、pow、powl
字符串拼接 .. +
Lua中没有的操作符:&&、||、!、++、--、-=、+=、/=、*=、%=

8.函数参数的值传递与地址传递

        百度搜到的说法:“Lua中除了table是引用传递外,其余基本都是值传递。”       

        如下所示的Swap函数无法满足交换变量的需求,而直接交换可以。

        所以我猜想——即使传入的参数是地址,但被修改的仍是Lua自动创建的同名local变量。

-- 用函数交换变量
function Swap(a, b)
    a, b = b, a
end

a, b = 2, 5
Swap(a, b)
print("a: "..a)
print("b: "..b) 

Add = function(a, b) return a + b end
Mul = function(a, b) return a * b end
Swap(Add, Mul)
print("Add(2, 5): "..Add(2, 5))
print("Mul(2, 5): "..Mul(2, 5))

t1, t2 = {2, 5}, {5, 2}
Swap(t1, t2)
print("t1: "..table.concat(t1, " "))
print("t2: "..table.concat(t2, " "))
-- 直接交换变量
a, b = 2, 5
a, b = b, a
print("a: "..a)
print("b: "..b) 

Add = function(a, b) return a + b end
Mul = function(a, b) return a * b end
Add, Mul = Mul, Add
print("Add(2, 5): "..Add(2, 5))
print("Mul(2, 5): "..Mul(2, 5))

t1, t2 = {2, 5}, {5, 2}
t1, t2 = t2, t1
print("t1: "..table.concat(t1, " "))
print("t2: "..table.concat(t2, " "))

        为了验证上述猜想,我下了下面两个程序,得到了一样的输出结果。至此,我认为下面两种写法是等价的(我是Lua新手,如有错误,欢迎指正)。

        总结为——无论是传入的参数是地址还是值,Lua都会自动创建该变量的同名local变量。

-- 交换table中的两个变量
function SwapPair(t)
    t[1], t[2] = t[2], t[1]
end

t = { 2,1,3 }
SwapPair(t)
print(table.concat(t, " "))
-- 交换table中的两个变量
function SwapPair()
    local _t = t
    _t[1], _t[2] = _t[2], _t[1]
end

t = { 2,1,3 }
SwapPair()
print(table.concat(t, " "))

9.判断条件

        Luafalsenil视为是false,其他的都视为true(注意,0也视为true)。


暂时先写到这里……


六. 函数

        返回可变参数的长度。

select('#', …)

        用于返回从起点 n 开始到结束位置的所有参数列表。

select(n, …) 

七. 运算符

1. 部分运算符

#	-- 返回字符串或表的长度; 相当于.size()

2. 运算符优先级

^
not      -(unary)
*        /        %
+        -
..
<        >        <=        >=        ~=        ==
and
or

八. 字符串

1. 常见操作

string.upper()
-- 转为大写字母

string.lower()
-- 转为小写字母

string.sub()
-- 字符串截取

string.gsub()
-- 字符串替换

string.find ()
-- 字符串查找

string.reverse()
-- 字符串反转

string.format()
-- 字符串格式化(类似于print)

string.len()
-- 获取字符串长度

string.rep()
-- 返回字符串的n个拷贝

..
-- 拼接两个字符串

string.gmatch()
-- 不完全匹配(类似于正则表达式)

string.match()
-- 完全匹配(类似于正则表达式)

string.byte()
-- 字符转整数

string.char()
-- 整数转字符

2. 匹配模式

        和正则表达式类似,语法比较晦涩但功能却十分强大。

九. 表(table)

table.concat ()
-- 列举元素

table.insert ()
-- 插入元素

table.remove ()
-- 移除元素

table.sort ()
-- 排序

十. 模块

        类似于头文件,需要用require()函数来加载模块,若要导入自定义的包,则需配置环境变量。

十一. 元表(matetable)

        和MySql里面的表有些类似,支持以下多种操作。

t1 + t2
t1 - t2
t1 * t2
t1 / t2
t1 % t2
-t1
t1..t2
t1 == t2
t1 < t2
t1 <= t2

        但是使用前需要声明操作符对应的元方法。 

        __newindex 元方法用来对表进行更新,__index则用来对表进行访问。__call 元方法在 Lua 调用一个值时调用。__tostring 元方法用于修改表的输出行为。

十二. 协程(coroutine)

        Lua 协程与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。

        协程与线程的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。协程类似于等待同一个线程锁的几个线程。

coroutine.create()
coroutine.wrap()
-- 创建协程

coroutine.resume()
-- 重启协程

coroutine.yield()
-- 挂起协程

coroutine.status()
-- 查看协程状态

coroutine.running()
-- 查看协程号

十三. 错误处理

        一般用assert()或者error()来排除错误,error()相当于C++里的noexcept()。

        另外可以用pcall 和 xpcall、debug来包装需要测试的代码,类似于try,catch,throw。

十四. 调试(Debug)

        Lua 没有内置的调试器,但提供了 debug 库提供我们创建自定义调试器的功能。

debug()
-- 进入一个用户交互模式,运行用户输入的每个字符串。
-- 输入一行仅包含 cont 的字符串将结束这个函数。

getfenv()
-- 返回对象的环境变量

gethook()
-- 返回线程钩子设置的值: 当前钩子函数,当前钩子掩码,当前钩子计数

getinfo ()
-- 返回关于一个函数信息的表

debug.getlocal ()
-- 此函数返回在栈的 f 层处函数的索引为 local 的局部变量 的名字和值。

getmetatable()
-- 把给定索引指向的值的元表压入堆栈

getregistry()
-- 返回注册表

getupvalue ()
-- 此函数返回函数 f 的第 up 个上值的名字和值

sethook ():
-- 将一个函数作为钩子函数设入

setlocal ()
-- 这个函数将 value 赋给 栈上第 level 层函数的第 local 个局部变量

setmetatable ()
-- 将 value 的元表设为 table

setupvalue ()
-- 这个函数将 value 设为函数 f 的第 up 个上值

traceback ()
-- 如果有 message , 且不是字符串或 nil, 返回 message

十五. 垃圾回收

        自动内存管理。 Lua 实现了一个增量标记-扫描收集器,有两个指标: 间歇率、步进倍率。 

        间歇率:控制着收集器需要在开启新的循环前要等待多久。

        步进倍率:控制着收集器运作速度相对于内存分配速度的倍率。

        collectgarbage("collect"): 执行一次垃圾回收器

        collectgarbage("count"): 统计变量占用内存的大小(多少K字节)

        collectgarbage("restart"): 重启垃圾回收器

        collectgarbage("setpause",arg): 将 arg 设为间歇率

        collectgarbage("setstepmul",arg): 将 arg 设为步进倍率

        collectgarbage("step"): 执行一步垃圾回收器

        collectgarbage("stop"): 停止垃圾回收器

十六. 面向对象

Empty = {x = 0, y = 0}

function Empty:new(o, x, y)
  o = o or {}
  setmetatable(o, self)
  self.__index = self
  self.x = x or 0
  self.y = y or 0
  return o
end

function Empty:LogSite()
  print("X: "..self.x, "Y: "..self.y)
end

empty = Empty:new(nil, 20, 50)
empty:LogSite()

Player = Empty:new()

function Player:new(o, x, y)
  o = o or Empty:new(0, x, y)
  setmetatable(o, self)
  self.__index = self
  return o
end

player = Player:new(nil, 50, 50)
player:LogSite()

        另外,可以用函数闭包的方式实现面向对象。 

function Empty(x, y)
  local self = {}
  local function init()
    self.x = x
    self.y = y
  end

  self.LogSite = function()
    print("X: "..self.x, "Y: "..self.y)
  end

  init()
  return self
end

empty = Empty(50, 50)
empty:LogSite()

function Player(x, y)
  local self = Empty(x, y)
  return self
end

player = Player(100, 100)
player:LogSite()

17. Lua补充代码

for alpha = 0, 1, 0.1 do
    print(alpha)
end

for alpha = 0, 1, 0.05 do
    print(alpha)
end

-- 第二种个for循环并没有输出1.0

你可能感兴趣的:(学习笔记,lua)