lua学习
一、注释
单行注释:两个破折号
多行注释:[[ … ]]
二、变量和流程控制
所有的数字类型都是double
num=42
字符串是Immutable的,和python一样
s = 'walternate'
t = "double-quotes are also fine"
u = [[ Double brackets
start and end
multi-line strings.]]
lua有垃圾回收机制,t是undefined
t = nil
语句块用关键字do/end标示
while num < 50 do
num = num + 1
end
语句:
if num > 40 then
print('over 40')
elseif s ~= 'walternate' then – ~= 是不等号.
– 等号是== 对字符串也适用.
io.write('not over 40n') – 默认输出到stdout.
else
– 变量默认是全局的.
thisIsGlobal = 5 – 常用Camel样式.
– 局部变量如下定义:
local line = io.read() – 读取下一个stdin 行.
– 字符串连接使用 … 操作符:
print('Winter is coming, ' … line)
end
未定义的变量返回nil
foo = anUnknownVariable – Now foo = nil.
aBoolValue = false
– 只有nil 和 false 是 falsy; 0 和 '' 是 true!
if not aBoolValue then print('twas false') end
– 'or' and 'and' 是短路的.
– 这和 C/js中的 a?b:c 相似:
ans = aBoolValue and 'yes' or 'no' –> 'no'
karlSum = <span class="number">0</span>
<span class="keyword">for</span> i = <span class="number">1</span>, <span class="number">100</span> <span class="built_in">do</span> <span class="comment">-- 范围是闭区间.</span>
karlSum = karlSum + i
<span class="function"><span class="keyword">end</span>
– 用 "100, 1, -1" 作为区间来递减:
fredSum = 0
for j = 100, 1, -1 do fredSum = fredSum + j end
– 总体上, 区间是 begin, end[, step].
– 另一种循环结构:
repeat
print('the way of the future')
num = num - 1
until num == 0
三、函数
function fib(n)
if n < 2 then return 1 end
return fib(n - 2) + fib(n - 1)
end
– 也可返回闭包和匿名函数:
function adder(x)
– 当adder被调用时,返回函数就被创建,并且保存有x的值:
return function (y) return x + y end
end
a1 = adder(9)
a2 = adder(36)
print(a1(16)) –> 25
print(a2(64)) –> 100
x, y, z = 1, 2, 3, 4
– 赋值后 x = 1, y = 2, z = 3, 但 4 被丢弃了.
function bar(a, b, c)
print(a, b, c)
return 4, 8, 15, 16, 23, 42
end
x, y = bar('zaphod') –> prints "zaphod nil nil"
– 现在 x = 4, y = 8, 值 15…42 被丢弃了.
– 函数是一等公民, 可以是 local/global.
– 下面是等价的:
function f(x) return x * x end
f = function (x) return x * x end
– 下面也是等价的:
local function g(x) return math.sin(x) end
local g; g = function (x) return math.sin(x) end
– 调用只有一个字符串的参数,不需要括号:
print 'hello' – Works fine.
四、表
Tables : 哈希查找的关联数组,和dictionaries、map类似,是lua仅有的复合数据结构。
Dict的的键默认是字符串的
t = {key1 = 'value1', key2 = false}
print(t.key1) -- 输出 'value1'.
t.newKey = {} -- 添加一个新的 key/value 对.
t.key2 = nil -- 从table中移除 key2.
表常用的的键一般是数字或者字符串
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28]) -- 输出"tau"
a = u['@!#'] -- a = 'qbert'.
b = u[{}] -- 误认为是1729, 其实是 nil。
for key, val in pairs(u) do -- 表迭代.
print(key, val)
end
-- 调用一个 one-table-param 的函数不需要括号
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'} -- Prints 'Sonmi~451'.
-- _G 是一个全局的特殊表.
print(_G['_G'] == _G) -- 输出 'true'.
-- 把 tables 用作 lists / arrays:
-- List 的键默认是整数,键是连续的整数,其实还是table:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do -- #v 是列表v的长度.
print(v[i]) -- 第一个索引是1
end
4.1. 元表和元方法.
–元表给予表操作符重载的特性,类似js的prototype的特性。
f1 = {a = 1, b = 2} -- 表示分数 a/b.
f2 = {a = 2, b = 3}
– 但是下面这样会报错:
– s = f1 + f2
metafraction = {}
function metafraction.__add(f1, f2)
sum = {}
sum.b = f1.b * f2.b
sum.a = f1.a * f2.b + f2.a * f1.b
return sum
end
setmetatable(f1, metafraction)
setmetatable(f2, metafraction)
s = f1 + f2
– 元表上的 __index 重载 . 查找:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal – 可行! thanks, metatable
一个__index值也可以是个函数(tbl, key),有利于个性化查找。
像__index,add, … 都是元方法,下面是一个全表,这里 a是一个有metamethod的表
– __add(a, b) for a + b
– __sub(a, b) for a - b
– __mul(a, b) for a * b
– __div(a, b) for a / b
– __mod(a, b) for a % b
– __pow(a, b) for a ^ b
– __unm(a) for -a
– __concat(a, b) for a … b
– __len(a) for #a
– __eq(a, b) for a == b
– __lt(a, b) for a < b
– __le(a, b) for a <= b
– __index(a, b) <fn or a table> for a.b
– __newindex(a, b, c) for a.b = c
– __call(a, …) for a(…)
4.2. 类风格的表与继承
类不是内置的,有多种可以使用tables和metatables的方法
Dog = {} -- 1.
function Dog:new() – 2.
newObj = {sound = 'woof'} – 3.
self.__index = self – 4.
return setmetatable(newObj, self) – 5.
end
function Dog:makeSound() – 6.
print('I say ' … self.sound)
end
mrDog = Dog:new() – 7.
mrDog:makeSound() – 'I say woof' – 8.
–1. Dog 像是一个class,它实际是一个表。
–2. 函数 tablename:fn(…) 和函数tablename.fn(self,…)一样,: 仅仅添加第一个名为self的参数。
–3. newObj是类Dog的实例。
–4. self就是被继承的类,但是继承可以改变self的值,这里是Dog,当我们设置newObj’s metatable 和 self’s __index to self时,newObj 才能使用self’s 函数。
–5. setmetatable 返回它的第一个参数
–6. 这时,self是一个实例而不是一个类。
–7. 和Dog.new(Dog)一样。
–8. 和mrDog.makeSound(mrDog)一样; 此时 self = mrDog.
继承的例子:
LoudDog = Dog:new() -- 1.
function LoudDog:makeSound()
s = self.sound .. ' ' -- 2.
print(s .. s .. s)
end
seymour = LoudDog:new() -- 3.
seymour:makeSound() -- 'woof woof woof' -- 4.
– 1. LoudDog 继承了 Dog’s 的方法和变量。
– 2. self 有一个来自于new()的 ‘sound’键。
– 3. 和LoudDog.new(LoudDog)一样, 由于LoudDog没有‘new’键,将被转换为 Dog.new(LoudDog),但是它的 metatable 拥有 index = Dog。
结论:seymour的 metatable是LoudDog,LoudDog.index = LoudDog. 所以 seymour.key = seymour.key, LoudDog.key, Dog.key, 任何第一个含有给定键的表。
– 4. ‘makeSound’ key 包含在LoudDog;这和LoudDog.makeSound(seymour)一样.
– 如果有需要, 一个子类的 new() 可以和基类的一样:
function LoudDog:new()
newObj = {}
-- set up newObj
self.__index = self
return setmetatable(newObj, self)
end
五、模块
– 假设文件mod.lua 像如下所示:
local M = {}
local function sayMyName()
print('Hrunkner')
end
function M.sayHello()
print('Why hello there')
sayMyName()
end
return M
– 另一个文件可以使用mod.lua's 的函数:
local mod = require('mod') – 运行文件 mod.lua.
– require 是 引用和包含 modules 的标准方法.
– require 其实像下面这样工作: (没有缓存的)
local mod = (function ()
<contents of mod.lua>
end)()
– mod.lua 像是一个函数体,所以 mod.lua内的局部变量对外不可见
– 因为在mod.lua里面,这里的mod = M :
mod.sayHello() – Says hello to Hrunkner.
– 错误; sayMyName 只存在于 mod.lua:
mod.sayMyName() – error
– require 的返回值是经过缓存的,所以一个文件最多运行一次,无论require多少次。
– 假设 mod2.lua 包含 "print('Hi!')".
local a = require('mod2') – 输出 Hi!
local b = require('mod2') – 无输出; a=b.
– dofile 就像无缓存的 require:
dofile('mod2.lua') –> Hi!
dofile('mod2.lua') –> Hi! (runs it again)
– loadfile 装载一个lua 文件,但并不立即运行。
f = loadfile('mod2.lua') – 调用 f() 来运行.
– loadstring 相当于strings版的loadfile.
g = loadstring('print(343)') – 返回一个函数.
g() – 输出343; 此前无输出.