learn lua

4.8k 词
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380


--[[
多行注释
--]]

----------------------------------------------------
-- 1. 变量和流程控制
----------------------------------------------------

num = 42 -- 所有的数字都是双精度浮点型。
-- 别害怕,64位的双精度浮点型数字中有52位用于
-- 保存精确的整型值; 对于52位以内的整型值,
-- 不用担心精度问题。

s = 'walternate' -- 和Python一样,字符串不可变。
t = "也可以用双引号"
u = [[ 多行的字符串
以两个方括号
开始和结尾。]]
t = nil -- 撤销t的定义; Lua 支持垃圾回收。

-- 块使用do/end之类的关键字标识:
while num < 50 do
num = num + 1 -- 不支持 ++ 或 += 运算符。
end

-- If语句:
if num > 40 then
print('over 40')
elseif s ~= 'walternate' then -- ~= 表示不等于。
-- 像Python一样,用 == 检查是否相等 ;字符串同样适用。
io.write('not over 40n') -- 默认标准输出。
else
-- 默认全局变量。
thisIsGlobal = 5 -- 通常使用驼峰。

-- 如何定义局部变量:
local line = io.read() -- 读取标准输入的下一行。

-- ..操作符用于连接字符串:
print('Winter is coming, ' .. line)
end

-- 未定义的变量返回nil。
-- 这不是错误:
foo = anUnknownVariable -- 现在 foo = nil.

aBoolValue = false

--只有nil和false为假; 0和 ''均为真!
if not aBoolValue then print('false') end

-- 'or'和 'and'短路
-- 类似于C/js里的 a?b:c 操作符:
ans = aBoolValue and 'yes' or 'no' --> 'no'

karlSum = 0
for i = 1, 100 do -- 范围包含两端
karlSum = karlSum + i
end

-- 使用 "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

----------------------------------------------------
-- 2. 函数。
----------------------------------------------------

function (n)
if n < 2 then return n 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

-- 返回值、函数调用和赋值都可以
-- 使用长度不匹配的list。
-- 不匹配的接收方会被赋值nil;
-- 不匹配的发送方会被丢弃。

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') --> 打印 "zaphod nil nil"
-- 现在 x = 4, y = 8, 而值15..42被丢弃。

-- 函数是一等公民,可以是局部的,也可以是全局的。
-- 以下表达式等价:
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
-- 以上均因'local g',使得g可以自引用。
local g = function(x) return math.sin(x) end
-- 等价于 local function g(x)..., 但函数体中g不可自引用

-- 顺便提下,三角函数以弧度为单位。

-- 用一个字符串参数调用函数,可以省略括号:
print 'hello' --可以工作。

-- 调用函数时,如果只有一个table参数,
-- 同样可以省略括号(table详情见下):
print {} -- 一样可以工作。

----------------------------------------------------
-- 3. Table。
----------------------------------------------------

-- Table = Lua唯一的组合数据结构;
-- 它们是关联数组。
-- 类似于PHP的数组或者js的对象,
-- 它们是哈希表或者字典,也可以当列表使用。

-- 按字典/map的方式使用Table:

-- Dict字面量默认使用字符串类型的key:
t = {key1 = 'value1', key2 = false}

-- 字符串key可以使用类似js的点标记:
print(t.key1) -- 打印 'value1'.
t.newKey = {} -- 添加新的键值对。
t.key2 = nil -- 从table删除 key2。

-- 使用任何非nil的值作为key:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28]) -- 打印 "tau"

-- 数字和字符串的key按值匹配的
-- table按id匹配。
a = u['@!#'] -- 现在 a = 'qbert'.
b = u[{}] -- 我们或许期待的是 1729, 但是得到的是nil:
-- b = nil ,因为没有找到。
-- 之所以没找到,是因为我们用的key与保存数据时用的不是同
-- 一个对象。
-- 所以字符串和数字是移植性更好的key。

-- 只需要一个table参数的函数调用不需要括号:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'} -- 打印'Sonmi~451'.

for key, val in pairs(u) do -- 遍历Table
print(key, val)
end

-- _G 是一个特殊的table,用于保存所有的全局变量
print(_G['_G'] == _G) -- 打印'true'.

-- 按列表/数组的方式使用:

-- 列表字面量隐式添加整数键:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do -- #v 是列表的大小
print(v[i]) -- 索引从 1 开始!! 太疯狂了!
end
-- 'list'并非真正的类型,v 其实是一个table,
-- 只不过它用连续的整数作为key,可以像list那样去使用。

----------------------------------------------------
-- 3.1 元表(metatable) 和元方法(metamethod)。
----------------------------------------------------

-- table的元表提供了一种机制,支持类似操作符重载的行为。
-- 稍后我们会看到元表如何支持类似js prototype的行为。

f1 = {a = 1, b = 2} -- 表示一个分数 a/b.
f2 = {a = 2, b = 3}

-- 这会失败:
-- s = f1 + f2

metafraction = {}
function metafraction.__add(f1, f2)
local 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 -- 调用在f1的元表上的__add(f1, f2) 方法

-- f1, f2 没有关于元表的key,这点和js的prototype不一样。
-- 因此你必须用getmetatable(f1)获取元表。
-- 元表是一个普通的table,