Lua

Lua学习笔记:基本语法

Posted by 蔡华的博客 on July 29, 2017

初步印象

  • 语法和python、js这些很像,解释型语言、弱类型等等。写着舒服是真的。
  • while do\repeat until这样的语法其实真心不舒服,感觉在语法上有些累赘。大多数语言都是do while,但是它非要是repeat until,也是无语。
  • 我个人认为语法中的then和do设计的很混淆。比如if是和then,for是和do。从我对语言的理解上感觉可以统一用do或者then。

基本语法

变量

  • 只有布尔、字符串、数字、table和nil。但是没有一个明确的类型定义,完全按照赋值来进行推断。
  • 默认是public,如果想private需要变量前加local。
  • 可以一次性多复制,如果变量和值不对称,没有复制的变量为nil。
a,b,c = 1,2
print(a)
print(b)
print(c)

--结果

1
2
nil

作用域

  • 在lua中一个local变量的作用域可以是在一个循环中或者在一段if代码中。也可以是贯穿整个的table中(感觉table在某种角度完全可以看做是一个类)。但是如果你想控制一个local变量的值作用域某个区间,那么可以使用do end来明确划分一个block。下面这个例子的结果是nil,因为x在作用域只在do end之间。但是这个不适用于全局变量,需要注意。
do
	local x = 10
end
print(x)

-- 结果

nil
  • 对比一下下面两段代码的不同结果。其实尽可能的不要出现变量同名的情况,尤其是第二段代码的行为可能不是我们想要的。
x = 1
do
	local x = 10
	print(x)
end
print(x)

-- 结果
10
1
x = 1

local x = 10
print(x)

print(x)

-- 结果
1
1
  • 关于do end还有个用法,在lua中return和break需要在end\else\until之前(发现在print之前也可以),否则编译会出错。此时就需要do end来包住return了。
function boo()
	local x = 10
	do
		return
	end
	local i = 10
	print(1)
end

循环

  • for\while do\repeat until,最后一个对象do while。
  • for分为数值型和泛型:
-- var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次"执行体"。exp3是可选的,如果不指定,默认为1。
for var=exp1,exp2,exp3 do  
    <执行体>  
end  
for i,v in ipairs(a) 
	do print(v) 
end  
  • for的循环中没有continue,这样导致了在for中可能出现if嵌套if的情况,因为没办法通过拆解if来实现简练的代码。不爽!!另外lua的

迭代

需要注意pairs 和 ipairs异同 同:都是能遍历集合(表、数组) 异:ipairs 仅仅遍历值,按照索引升序遍历,索引中断停止遍历。即不能返回 nil,只能返回数字 0,如果遇到 nil 则退出。它只能遍历到集合中出现的第一个不是整数的 key。 pairs 能遍历集合的所有元素。即 pairs 可以遍历集合中所有的 key,并且除了迭代器本身以及遍历表本身还可以返回 nil。

Demo1:
local tabFiles = {
        [1] = "test2",
        [6] = "test3",
        [4] = "test1"
    }

for k, v in ipairs(tabFiles) do    --输出"test2",在key等于2处断开
    print(k, v)
end

Demo2:
local tabFiles = {
    [2] = "test2",
    [6] = "test3",
    [4] = "test1"
}

for k, v in ipairs(tabFiles) do  --[[什么都没输出,为什么?因为控制变量初始值是按升序来遍历的,当key为1时,value为nil,此时便停止了遍历, 所有什么结果都没输出]]--
    print(k, v)
end

Demo3:
local tabFiles = {
    [2] = "test2",
    [6] = "test3",
    [4] = "test1"
}

for k, v in pairs(tabFiles) do  --输出2 test2, 6 test3, 4 test1
    print(k, v)
end

Demo4:
local tabFiles = {"alpha", "beta", [3] = "no", ["two"] = "yes"}  for i,v in ipairs(tabFiles ) do    --输出前三个   备注:因为第四个key不是整数
    print( tabFiles [i] )   
end   
  
for i,v in pairs(tabFiles ) do    --全部输出   
    print( tabFiles [i] )   
end 

table

概念

table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数字、字典等。

Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。

Lua table 是不固定大小的,你可以根据自己需要进行扩容。

  • 上面是runoob.com的lua教程中对table的描述。在我看来很难去描述table是个什么东西,但是我们可以通过对它的使用来总结它所扮演的一些角色,或者说什么场景下table能够作为什么来使用。
  • 看下第二条,table中索引可以是任何类型的值,所以索引是个table也是可以的。但是必须不是nil,所以look = {[www] = "ok"} 这样是不对的,www没有赋值,所以默认为nil因此出错table index is nil

常见用法

  • 数组 : table一个典型的用法就是作为其它语言中的数组
str = { 1,2,3}
print(str[2])
  • 字典 : 也是常见的用法
str = {name="Sai",age=33}
print(str["age"])
print(str.name)

table使用中容易出现的问题

  • 看下这段代码
temp = 1
tab = {[temp] = 1, 11}
print(tab[1])
for i,v in pairs(tab) do
	print(i,v)
end

-- 输出的结果是 : 

11
1	11
  • why?第一个print中为何结果不是1,因为tab[]这个用法的参数是key,而像11这样没有显示定义key的value默认的key就是1,所以这句话认为是输出key=1的value了。而且从遍历看居然只有一个数据,也就是说value 1的数据被替换掉了。
  • 那么怎么让这个1显示出来呢?以我目前掌握的只能是改temp的值。
  • 总结一下就是注意三点:

第一,所有元素之间,总是用逗号 “,” 隔开;

第二,所有索引值都需要用 “[“和”]”括起来;如果是字符串,还可以去掉引号和中括号; 即如果没有[]括起,则认为是字符串索引

第三,如果不写索引,则索引就会被认为是数字,并按顺序自动从 1往后编;

  • 数组(lua叫table)下标从1开始,有点不习惯。

  • 判断table是否是空(参考)

a={2}
if next(a) ~= nil then
	print(next(a))
end

字符串

  • ..是字符串相加,my god…
  • 对于string的处理,除了用string.xxx(strArg)这样的方式外,还可以用strArg:xxx的方法。

函数

  • function可以返回也可以不返回结果,在定义时没有明确的约束。
  • 支持多返回值,nice
  • Lua函数不支持参数默认值,可以通过or来实现。
function sum(a , b)
    a = a or 1.1
    b = b or 2.2
    return a + b
end
 
print(sum())
  • 支持闭包
tab = {1,2,3,4,5,6,7,8}
 
function iter()
    local index = 0
    return function()
        index = index + 1 
        return tab[index]
    end
end
 
for i in iter() do
   print(i)
end
  • 可变参数,这里有点小特别:

    Lua将函数的参数放在一个叫arg的表中,#arg 表示传入参数的个数。

function average(...)
   result = 0
   local arg={...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. #arg .. " 个数")
   return result/#arg
end

print("平均值为",average(10,5,3,4,5,6))

这个#还能来取table中元素的个数,但是使用限制比较大。建议还是开启循环来计算个数。

  • :.的区别:

    是个语法糖,调用的函数会自动传递参数self,即:

local a = {x = 0}
function a.foo(self, a)
    self.x = a
end
function a:foo2(a)
    self.x = a
end


--调用时:
a.foo(a, 2)
a.foo2(2)

--上述两个操作是等价的,用:时就省去了定义和调用时需要额外添加self用来指代自身的麻烦.

  • 需要注意的是如果一个table中的方法调local的非table方法(简单来说就是一个类方法要访问非类方法)需要将非类的方法定义在前面,这个和C的机制一样。同样的,非类方法之间的调用也要有顺序,都尼玛血泪的教训

API

  • 标准库的API可以在这里查询到。
  • math.random的行为很是奇怪,一次调用永远产生相同的值。如果想得到不同的值,需要多次调用。
  • 对于table等的操作建议看参考2的连接。