ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddvddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddv
Trang 1 Lua được viết bằng C với hơn hai chục nghìn dòng code!
Trình thông dịch Lua chỉ có kích thước vài trăm KB.
Bộ nhớ mà Lua sử dụng khi chạy cũng rất ít.
Nhanh hơn Python không?
Bộ xử lý Lua bao gồm trình biên dịch, dịch code thành bytecode và máy ảo để thực thi code đó
Kiến trúc máy ảo của Lua từ v5.x trở đi sử dụng mô hình thanh ghi theo kiến
trúc x86 (trước đó là sử dụng mô hình ngăn xếp)
Tất nhiên là nhanh hơn Python nhiều (có thể nói là nhanh nhất trong các họ ngôn ngữ kịch
bản phổ biến) Lua có một phiên bản tên là LuaJIT, thay vì dịch ra bytecode thì nó dịch ra mã
máy.
Không so sánh với CPython, PyPy mà có gán mác JIT vào cũng thua LuaJIT, chấp V8 luôn.
Trang 2Ngôn ngữ kịch bản luôn gắn liền với từ thông dịch, ở đây Lua cho code vào và chạy luôn.
Xuất hiện thêm từ biên dịch là do Lua dịch code sang bytecode rước khi cho vào máy ảo.
Đa hình ra sao?
Lua hỗ trợ lập trình hàm và cả hướng đối tượng.
OOP chủ yếu thông qua prototype, tựa tựa JS vậy.
Làm được gì mà đa tính năng?
Lua có thể dùng để viết ứng dụng, game, web (backend) và cả hệ thống.
Lua xuất hiện nhiều trong cả lập trình nhúng, đi kèm với các ngôn ngữ nhanh như C/C+
+, Rust để dễ dàng triển khai, tái sử dụng các API.
Có thể tham khảo các project về Lua tại đây , ở đây và cả đây nữa
Nhúng ở đây không phải là lập trình mạch mà là Lua hỗ trợ CAPI để nhúng vào các ngôn ngữ
just-in-Ngoài ra còn có một IDE được viết hoàn toàn bằng Lua (sử dụng wxWidget UI) vô cùng nhanh và
nhẹ, đó là ZeroBrane Studio , nó hỗ trợ chủ yếu là code Lua và các game engine có Lua built-in, có live-coding khá ngon.
Trên Windows, ta có thể sử dụng Lua Studio , trọn gói cài đặt chỉ bằng giao diện.
Lua có một package manager tên là LuaRocks , tựa tựa như vcpkg, conan hỗ trợ cài đặt các thư
viện ngoài nhanh chóng.
Trang 3Hello Lua
$ echo print("Hello, world!")> test.lua
Hoặc tạo một file và gõ code sau vào:
print("Hello, world!")
Trên một số code editor/IDE chỉ cần nhấn Ctrl + B hoặc chạy lệnh sau:
$ lua /test.lua
1 Chú pháp và biến
Note: Các hướng dẫn được viết trong code block, bạn có thể copy chúng và chạy Hãy cẩn thận vì
có tiếng Việt, không nên set file test với encoding là UTF8 with BOM.
Hai dấu trừ đánh dấu comment trên một dòng
Khai báo biến a, nếu không gán gì thì nó là nil
nil có thể hiểu là chưa định nghĩa, không mang giá trị nào
a =4
Gán giá trị cho biến
Trang 4Số trong Lua đều là số thực double,
nhưng biến a vẫn được hiểu là số nguyên
print để in giá trị ra console
Biến b chưa được khai báo sẽ là nil, không lỗi đâu vì nó được hiểu là biến global
Trang 5Khai báo một chuỗi.
s ='dấu nháy đơn'
n ="dấu nháy kép cũng được"
u =[[
chuỗi trên
nhiều dòng bằng
hai hai cặp ngoặc vuông]]
Nối hai chuỗi bằng hai dấu chấm
x ='abc' 'def'
print(x '789')
p = print Tuy print là một hàm, nhưng cũng là kiểu dữ liệu
print =20 Tất cả các biến đều thay đổi giá trị được
p(print) In ra 20
print = p Trả lại như cũ
2 Rẻ nhánh và câu điều kiện
b =true Kiểu đúng/sai boolean
if b then
print('yes')
Trang 6còn false và nil đều sai.
Logic or, and
print(falseandnil) > false
print(0andnil) > nil
Trang 7print(trueandnil) > nil
print(falseand0) > false
print(trueandfalse) > false
Trang 8foo() In ra Hello World!
Khai báo kiểu lambda
Trang 9print(x, y, z) Kiểm tra, chỉ in ra 4 5 6.
Thêm local vào trước thì là hàm cục bộ
localfunctiong(x)print(x)end
local g; g =function(x)print(x)end
Hai thằng như nhau
g((((((1))))))
g 'qwerty' In ra qwerty
Trường hợp gọi hàm không cần cặp ngoặc tròn khi tham số được cách tên hàm bằng các kí tự cú pháp
Trang 10như: ngoặc nhọn { (table), dấu nháy " hoặc ' (chuỗi).
Đây là kiểu cấu trúc phức hợp của Lua,
tương tự như array trong PHP hay object trong JS
t ={key1 ='value1', key2 =false}
Sử dụng dấu chấm để tham chiếu đến phần tử con
print( key1) In ra value1
t.newKey ={} Thêm newKey, giá trị là một table
t.key2 =nil Xóa key2
Sử dụng giá trị để đánh dấu phần tử con
u ={['@!#']='qbert',[{}]=1729,[6.0+0.28]='concho'}
Sử dụng cặp ngoặc [ ] và cho biểu thức vào trong
Trang 11print(u[6.28]) In ra concho.
a = u['@!#'] a = 'qbert'
b = u[{}] b = nil, không phải 1729 nhé
Vì key này là table rỗng,
cần phải sử dụng hai table giống nhau
Sử dụng table cho tham số của hàm
function h(x)print(x.key1)end
h {key1 ='AK-47'} In ra AK-47
Mảng (list / arrays):
v ={'value1','value2',1.21,'gigawatts'}
for i =1,#v do #v để lấy độ dài, dùng được cho cả chuỗi
print(v[i]) Array trong Lua "start at 1" nhé! =))
Trang 13r1 = f1 + f2 Gọi add(f1, f2) trong metatable của f1.
r2 = f2 + f1 Tương tự luôn, metatable của f2
print(r1.x, r1.y) > 7 -5
print(r2.x, r2.y) > 7 3
Hiểu đơn giản hơn thì một table bình thường,
chỉ để chứa dữ liệu mà thôi,
metatable thêm vào sẽ là bộ tiền xử lý cho nó
Thêm table vào table thông qua key index
defaultFavs ={animal ='dog', food ='donuts'}
myFavs ={food ='pizza'}
setmetatable(myFavs,{ index = defaultFavs})
eatenBy = myFavs.animal > 'dog'
Giá trị của index, add, được gọi là metamethod
Trang 14Full list, a là một table với một metamethod.
Trang 15Dog ={} 1.
function Dog:new() 2
newObj ={sound ='woof'} 3
self. index = self 4
returnsetmetatable(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 giống như một class, nhưng thực chất là table 2 Hàm tablename:func( ) được hiểu như hàm tablename.func(self, )
dấu hai chấm : thêm self vào
thành tham số thứ nhất
Đọc 7 & 8 bên dưới để hiểu nó thêm như nào
Trang 163 newObj sẽ là instance mới cho class Dog.
4 self = class mới tạo ra Thông thường
self = Dog, nhưng phần kế thừa sẽ thay đổi nó
newObj get các hàm từ chính nó nhưng set
metatable của newObj và index của nó
vào chính nó (Dog)
5 Nên nhớ: setmetatable sẽ return tham số đầu
6 Dấu hai chấm : như trong mục 2, nhưng self này
có thể sử dụng cho lớp con kế thừa thông qua Dog.new()
7 Giống như Dog.new(Dog), self = Dog trong new(). 8 Giống như mrDog.makeSound(mrDog); self = mrDog
- Ví dụ kế thừa
-LoudDog = Dog:new() 1
function LoudDog:makeSound()
Trang 17s = self.sound ' ' 2.
print(s s)
end
seymour = LoudDog:new() 3
seymour:makeSound() > 'woof woof' 4
1 LoudDog lấy method & property từ Dog
2 Bản thân nó có key 'sound' từ hàm new()
3 Giống như LoudDog.new(LoudDog),
và được chuyển thành Dog.new(LoudDog),
LoudDog không có key 'new', nhưng nó có
index = Dog trong metatable của nó (LoudDog)
Kết quả: metatable của seymour là LoudDog, và
LoudDog. index = LoudDog Nên seymour.key sẽ
= seymour.key, LoudDog.key, Dog.key, bất kỳ
table nào cũng là cái đầu tiên với key nhận được
4 Key 'makeSound' có sẵn trong LoudDog, seymour kế thừa, nó giống như LoudDog.makeSound(seymour)
Trang 18Nếu cần tạo thêm class con mới, cấu trúc sẽ như sau:
function LoudDog:new()
newObj ={} tạo newObj
self. index = self
returnsetmetatable(newObj, self)
end
8 Tạo module
Module giống như một thư viện riêng, và người dùng có thể sử dụng nó thông qua:
local mod =require('module_name')
Có 2 loại module bao gồm:
Lua module: viết bằng Lua, được xem như một hàm.
C module: thường được viết bằng C hoặc bất kỳ ngôn ngữ nào, được biên dịch dưới dạng
thư viện liên kết động.
Đầu tiên, tạo một file mới làm module:
Khởi tạo table M
Đây là biến cục bộ, chỉ sử dụng được trong file này
Trang 19M.foo =function(self)
Tiếp tục tạo một file mới để chạy code:
local mod =require('module')
Load module ở trên, lưu ý tên file
Nếu file module.lua nằm trong thư mục con thì
require('folder.module')
có thể thay '.' thành '/' hoặc '\\'
print(mod.a) > 1
print(mod.b) > true
mod:foo() > 'Hello'
Hoặc mod.foo(mod)
Nên nhớ, biến local trong main.lua sẽ không dùng được trong module.lua Nếu quy về hàm hoặc
sử dụng cho cùng một file thì sẽ như này:
localfunctionmodule()
Trang 20tất nhiên là không lưu các biến từ file đó,
không giống như require đâu
local f =loadfile('abc.lua')
loadfile sẽ load một file nhưng không chạy ngay mà lưu vào hàm
Trang 21os.exit() Thoát nhanh.
9 Lập trình coroutines
Một coroutine mang nghĩa một luồng (thread), lập trình coroutines cũng mang nghĩa lập trình đồng thời (concurrency, nhưng chỉ là nghĩa bóng) Khác nhau ở đây là nghĩa đen, là sự hợp tác (chứ không phải đồng thời) giữa các coroutine, và một lúc chỉ chạy được một coroutine, chúng có thể được yêu cầu dừng để coroutine khác chạy xen vào.
concurrency @ là điểm bắt đầu
coroutine @ o @ o
Coroutines chỉ khác đơn luồng ở chỗ là có thể dừng hàm ở bất kỳ đâu để xen hàm khác tại vị trí đó.
Trang 22Mấy con cẩu cute :v
Cho dễ hiểu hơn, hãy xem ảnh trên (biễu diễn cho concurrency) Khi gọi thì cả ba con cún đều xuất hiện cùng lúc, nhưng bạn chỉ có thể nựng một con một lúc Khi đơn luồng, bạn sẽ nựng mỗi
con một lúc, hết con này mới đến con khác Còn coroutines thì nếu trong lúc nựng con này mà
chán, bạn có thể sang nựng con khác, chán nữa thì quay lại con này cho đến khi xong.
Tạo một coroutine đơn giản như sau:
co = coroutine.create(function()
print("hi")
end)
print(co)
In ra thread: 0x64 , địa chỉ của coroutine.
Một coroutine sẽ có 3 trạng thái: suspended, running và dead.
print(co)
print(coroutine.status(co))
In ra suspended, nó đang ở trạng thái dừng vì chưa chạm gì vào.
Chạy và check trạng thái cuối cùng:
Trang 23coroutine.resume(co)
print(coroutine.status(co))
In ra dead, và tất nhiên sẽ không thễ resume được nữa.
Để dừng một coroutine đang chạy, ta có hàm coroutine.yield:
co = coroutine.create(function()
Nếu quá 10 lần resume thì nó dead.
Truyền tham số vào hàm trong coroutine:
co = coroutine.create(function(a,b,c)
print("co", a, b, c)
Trang 24coroutine.resume(co,1,2,3) > In ra co 1 2 3
Tính hợp tác
Ta có 2 coroutine sau:
co1 = coroutine.create(function()
for i=1,3doprint("co1_" i)end
end)
co2 = coroutine.create(function()
for i=1,3doprint("co2_" i)end
end)
Chạy thử:
end)
coroutine.resume(co1)
coroutine.resume(co2)
Ta sẽ thấy, nó chạy xong co1 rồi mới đến co2:
Trang 25Tiếp tục thêm yield vào, co1 dừng co2 và ngược lại:
co1 = coroutine.create(function()
for i=1,3doprint("co1_" i) coroutine.yield(co2)end
end)
co2 = coroutine.create(function()
for i=1,3doprint("co2_" i) coroutine.yield(co1)end
end)
Sửa luôn chỗ test:
end)
for i=1,3do
coroutine.resume(co1)
coroutine.resume(co2)
Đến đây là kết thúc rồi, có thể là hơn 30 phải không?
Lua rất dễ và thú vị, xứng đáng là ngôn ngữ cho người mới bắt đầu.
Khoảng 10 năm trước thì Lua bắt đầu nổi lên trong mảng lập trình game, đến nay nó vẫn đứng top!