25. Lua使用do…end宣告區域區塊
-- main file chunk is anywhere in the file
do
-- local chunk
end
do
-- a different local chunk
end
local關鍵字限制變數只在它所出現的區塊內有效
Scope 2/5
25
26. x = 3 -- x is accessable in the global chunk
print (x) -- will print: 3
print (y) -- will print: nil
print (z) -- will print: nil
local y = 4 -- y is accessable in the file chunk
-- The local keyword makes y local to the file
do
local z = 5 -- z is in a local chunk, in this example
-- that means local to the do/end block
print (x) -- will print: 3
print (y) -- will print: 4
print (z) -- will print: 5
end
print (x) -- will print: 3
print (y) -- will print: 4
print (z) -- will print: nil
Scope 3/5
26
27. 如果省略local關鍵字,則無論該變數是在哪個區塊中宣告,該變數
都是全域變數
x = 3 -- x is accessable in the global chunk
print (x) -- will print: 3
print (y) -- will print: nil
print (z) -- will print: nil
local y = 4 -- y is accessable in the file chunk
-- The local keyword makes y local to the file
do
z = 5 -- z is accessable in the global chunk
print (x) -- will print: 3
print (y) -- will print: 4
print (z) -- will print: 5
end
print (x) -- will print: 3
print (y) -- will print: 4
print (z) -- will print: 5
Scope 4/5
27
28. 區塊內宣告的區域變數名稱可以與全域變數相同名稱,在區塊內使用
的是區域變數,區塊外則使用全域變數,稱為variable shadowing
message = 'global-scope'
print ('message: ' .. message) -- Should print: global-scope
do
-- Shadow the message variable
local message = 'local-scope'
print ('message: ' .. message) -- Should print: local-scope
end
print ('message: ' .. message) -- Should print: global-scope
Scope 5/5
28
30. Defining a function
以function關鍵字開頭。在function關鍵字之後,提供函數名稱
函數名稱之後,必須提供參數列表。參數是用小括號()括起來的變數。
如果函數不需要參數,則參數列表可以為空,但仍需保留小括號()
聲明參數列表後,開始編寫函數的主體。函數主體是⼀段程式碼,因此,
與其他區塊⼀樣,需要使用end關鍵字關閉主體
function PrintSomething()
text1 = 'hello'
text2 = 'world'
print (text1 .. ', ' .. text2)
end
Functions 2/7
30
31. Calling a function
定義函數後,就可以通過調用它來執行
調用函數時,只需鍵入其名稱,然後加上括號。例如透過調用io.read(),
從控制台讀取輸入
function PrintSomething()
text1 = 'hello'
text2 = 'world'
print (text1 .. ', ' .. text2)
end
PrintSomething() -- call the function
PrintSomething() -- call the function
Functions 3/7
31
32. Function arguments
在定義函數時,可以在括號內放置⼀個或多個變數。這些變數是函數參數,
它們的作用區域就是函數區間
-- Declare the function, takes two arguments
function AddAndPrint(x, y)
local result = x + y;
print (x .. '+' .. y .. '=' .. result)
end
-- Call the function a few times
AddAndPrint(2, 3)
AddAndPrint(4, 5)
AddAndPrint(6, 7)
Functions 4/7
32
33. Any number of arguments
在Lua中,調用函式時不必提供與函式宣告相同數量的參數。多餘的參數
會被忽略,少給的參數值則為nil
-- Declare the function, takes two arguments
function AddAndPrint(x, y)
local result = x + y;
print (x .. '+' .. y .. '=' .. result)
end
-- Call the function a few times
AddAndPrint(2, 3) -- Will print 2+3=5
AddAndPrint(4, 5, 8, 9, 10) -- Will print 4+5=9
-- Declare the function, takes two arguments
function PrintValues(x, y)
print ('x: ' .. tostring(x) .. ', y: ' .. tostring(y))
end
-- Call the function a few times
PrintValues(3, 4) -- will print x: 3, y: 4
PrintValues(1) -- will print x: 1, y: nil
PrintValues() -- will print x: nil, y: nil
Functions 5/7
33
34. Returning a value
函式不僅接受輸入,還可以使用return將⼀些輸出返回給調用代碼。當函
式返回值時,可以將其作為運算式的⼀部分或獨立的指令敘述來調用
-- Declare the function
function SquareIt(number)
result = number * number
return result
end
-- Call the function
four = SquareIt(2)
print(four) -- Will print: 4
注意:
Lua語言之return指令之後必須是end指令
Functions 6/7
34
35. Returning multiple values
Lua允許⼀個函式返回多個值。要返回多個值,只要將return回傳值間使
用逗號分隔
與函式參數⼀樣,函式回傳值的個數不必與分配給它的變數數量匹配。如
果返回兩個值,但嘗試將它們分配給三個變數,額外變數的默認值為nil
-- Declare the function
function SquareAndCube(x)
squared = x * x
cubed = x * x * x
return squared, cubed
end
-- Call the function
s, c = SquareAndCube(2)
print ('Squared: ' .. s) -- will print: Squared: 4
print ('Cubed: ' .. c) -- will print: Cubed: 8
s, c, q = SquareAndCube(2) -- Call the same function
print ('Quartic: ' .. tostring(q)) -- will print: Quartic: nil
Functions 7/7
35
36. Closures是⼀種透過匿名函式把區域變數與相關聯程式封裝閉合的機制,
能保存函式被建立時的執行環境(context),這「閉鎖」的環境中保存了
讓函數可以持續存取(甚⾄在function被return後)的獨立自由變數
function NextNumber()
local currentNumber = 0 -- local to the NextNumber function
return function () -- anonymous function
-- 因為這個匿名函式是建立在NextNumber函式內部,它可以看到NextNumber函式的全部成員
-- 並記錄其狀態,建立閉包
currentNumber = currentNumber + 1
return currentNumber
end
end
-- Assign the anonymous function to the variable next
next = NextNumber() -- create closure
print(next()) -- 1
print(next()) -- 2
print(next()) -- 3
-- currentNumber does not exist in a global context!
print(currentNumber) -- nil
Closures 1/2
36
37. 下列程式是使用Closures建立陣列的Iterator functions
days = { 'monday', 'tuesday', 'wednesday', 'thursday' }
function walk(array)
local index = 0
return function()
index = index + 1
return array[index]
end
end
for day in walk(days) do
print (day) -- 依序輸出4行星期名稱
end
Closures 2/2
37
39. Arithmetic operators
addition operator (+)
subtraction operator (-)
multiplication operator (*)
division operator (/)
modulus operator (%)
x = 5 % 2 -- result is 1
y = 5.7 % 2 -- result is 1.7
z = 5.3 % 2.9 -- result is 2.4
exponent operator (^)
運算子 1/5
39
40. Relational operators
equality operator (==)
inequality operator (~=)
greater than operator (>)
greater than or equal to operator (>=)
less than operator (<)
less than or equal to operator (<=)
運算子 2/5
40
41. Logical operators
and operator (and)
如果第⼀個運算元為false,則回傳其第⼀個運算元;如果第⼀個運算元
為true,則回傳第二個運算元
x = true and false -- value is false
y = false and false -- value is false
z = true and true -- value is true
w = 7 and 1 -- value is 1
or operator (or)
如果第⼀個運算元為true,則回傳其第⼀個運算元;如果第⼀個運算元為
false,則回傳第二個運算元
x = true or false -- value is true
y = false or false -- value is false
z = true or true -- value is true
w = 7 or 1 -- value is 7
and/or運算都使用快捷評估。換言之,必要時才會評估第二個運算元
運算子 3/5
41
42. not operator (not)
x = not true -- false
y = not true or false -- false
z = not not false -- false
w = not (7 + 1) -- false
運算子 4/5
42
44. if敘述
if condition then
code block
end
print('Enter your name')
name = io.read()
if #name <= 3 then
print("that's a short name, " .. name)
end
流程控制 1/4
44
45. if … else敘述
if condition then
code block 1
else
code block 2
end
print('Enter a number')
x = io.read()
if x % 2 == 0 then
print(x .. ' is even')
else
print(x .. ' is odd')
end
流程控制 2/4
45
46. if … elseif敘述
if condition 1 then
code block 1
elseif condition 2 then
code block 2
...
elseif condition n then
code block n
else
code block
end
流程控制 3/4
46
47. print('Enter your name')
name = io.read()
if #name <= 3 then
print('that's a short name, ' .. name)
elseif #name <= 6 then
print(name .. ' is an average length name')
else
print(name .. ' is a long name')
end
流程控制 4/4
47
48. while迴圈
while condition do
-- chunk
end
x = 10 -- Initialize a 'control' variable
while x > 0 do -- Boolean condition: x > 0
print('hello, world')
x = x - 1 -- Decrement the 'control' variable
end
迴圈 1/4
48
49. break指令可強制中止迴圈 (須搭配if指令)
x = 0
while x < 10 do -- Execute 10 times!
print('x is ' .. x)
if x == 5 then
break -- This stops the loop execution at 5
end
x = x + 1
end
迴圈 2/4
49
50. repeat迴圈
repeat
-- chunk
until condition
x = 10 -- Initialize a 'control' variable
repeat
print('hello, world')
x = x - 1 -- Decrement the 'control' variable
end x == 0 -- Boolean condition: x == 0
迴圈 3/4
50
51. for迴圈
for variable = initial_exp, final_exp, step_exp do
-- chunk
end
for i = 0, 10, 1 do
print(i)
end
迴圈 4/4
51
52. table是Lua唯⼀的資料結構
建立table
table內容須置於大括弧內
tbl = {} -- Creates table, assigns it to tbl variable
print('The type of a table is: ' .. type(tbl))
table內容為鍵-值對 (key-value pair),每組資料以逗號分隔
key1 = value1, key2 = value2, ...
若鍵為數值或單⼀字元,則必須置於中括弧內
vector1 = {[1]='x', [2]='y', [3]='z', [4]=23}
當鍵值是數值資料,table作用為陣列資料;
vector = {'x', 'y', 'z', 23} -- 使用預設key,數值1, 2, 3, 4
當鍵值混雜非數值資料,table作用為字典資料
colors = {red = '#ff0000', green = '#00ff00', blue = '#0000ff'}
tables 1/5
52
55. 對⼀般變數⽽言,將變數指定給另⼀個變數,每個變數各別擁有自⼰
的副本
x = 10 -- y assigned 10 by value
y = x -- y assigned the value of x (10) by value
x = 15 -- x assigned 15 by value
print(x) -- 15
print(y) -- 10
tables 4/5
55
56. 將table變數指定給另⼀個變數,兩個變數是引用同⼀份table資料
a = {} -- a is assigned a table reference
b = a -- b references the same table as x
print(a)
print(b)
b.x = 10 -- also creates a.x, a and b reference the same table
a.y = 20 -- also creates b.y, a and b reference the same table
a.x = 30 -- also changes b.x, a and b reference the same table
print('a.x: ' .. a.x) -- print a.x: 30
print('b.x: ' .. b.x) -- print b.x: 30
print('a.y: ' .. a.y) -- print a.y: 20
print('b.y: ' .. b.y) -- print b.y: 20
a = nil -- a no longer references the table
b = nil -- nothing references the table after this
當變數a和b都設置為nil,table內容已沒有被任何變數引用,系統就可對
它進行垃圾回收
tables 5/5
56
58. 建立table時,若全部未指定鍵值,會自動形成陣列
vector = { 'x', 'y', 'z', 23}
print(tostring(vector[0])) -- nil, the array starts at 1
print(vector[1]) -- first element, x
print(vector[2]) -- second element, y
print(vector[3]) -- third element, z
print(vector[4]) -- fourth element, 23
Arrays 2/4
58
59. 大部分程式語言,陣列索引值從0開始,你可以透過下列方式來達成,
但這樣做違反Lua慣用法,使用#運算子時可能造成難以捉摸的細微錯
誤
vector = { [0] = 'x', 'y', 'z', 'w', 23 }
print(vector[0]) -- element before first, x
print(vector[1]) -- first element, y
print(vector[2]) -- second element, z
print(vector[3]) -- third element, w
print(vector[4]) -- third element, 23
Arrays 3/4
59
60. lua陣列可以是稀疏的(Sparse arrays),即陣列中可以元素無資料,
這些位置的值為nil
arr = { }
arr[1] = 'x'
arr[2] = 'y'
-- arr[3] is nil by default
-- arr[4] is nil by default
arr[5] = 'z'
arr[6] = 'w'
for i = 1, 6 do
print(tostring(arr[i]))
end
Arrays 4/4
60
62. lua不支援多維陣列,但可以透過建立an array of arrays (實際
上是a table of tables)來達成
num_rows = 4
num_cols = 4
values = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'}
value = 1
matrix = {} -- create new matrix
for i = 1, num_rows do
matrix[i] = {} -- create new row (matrix)
for j = 1, num_cols do
-- current element: row i, column j
matrix[i][j] = values[value] -- assign element to column
value = value + 1 -- move to next letter
end
end
print(matrix[1][1]) -- A
print(matrix[2][4]) -- H
print(matrix[3][4]) -- L
多維陣列
62
64. array資料使用ipairs遍覽函式
days = { 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday',
'sunday' }
for i, v in ipairs(days) do
print('index: ' .. i .. ', value: ' .. v)
end
Iterating 2/2
64
67. getmetatable方法用來讀取meta table設定值
x = {}
y = {}
z = {}
setmetatable(y, z)
setmetatable(z, z)
print(getmetatable(x)) -- nil
print(getmetatable(y)) -- random-looking number is the unique ID for table z
print(getmetatable(z)) -- random-looking number is the unique ID for table z
Mata tables 3/3
67
68. 在meta table中具有特定名稱的元方法
__index
讀取資料表中不存在的資料欄時,則會觸發執行__index元方法。如果不
存在__index元方法,則返回nil;如果存在,則返回結果
x = {}
z = {foo = 'bar'}
setmetatable(x, z)
print(x.foo) -- nil
print(z.foo) -- bar
Mata methods 1/10
68
69. x = {}
z = {
foo = 'bar',
__index = function(table, key)
return z[key]
end
}
setmetatable(x, z)
print(x.foo) -- bar, lives in z
print(z.foo) -- bar
Mata methods 2/10
69
70. __newindex
當對表中不存在的資料欄進行賦值時,就會自動調用__newindex元方法建
立鍵-值對
x = { }
z = {
__index = function(table, key)
return z[key]
end,
__newindex = function(table, key, value)
z[key] = value
end
}
setmetatable(x, z)
x.foo = 'bar' -- 由於x沒有foo成員,會調用其meta table中的__newindex方法
print(x.foo) -- bar, lives in z
print(z.foo) -- bar
Mata methods 3/10
70
72. x = { }
y = { }
z = {
__index = function(table, key)
return z[key]
end,
__newindex = function(table, key, value)
z[key] = value
end
}
setmetatable(x, z)
setmetatable(y, z)
x.foo = 'bar' -- Sets 'bar' in z
print(x.foo) -- bar, lives in z
rawset(x, 'foo', 'raw') -- Sets 'raw' in x directly!
print(x.foo) -- raw, lives in x
print(y.foo) -- bar, lives in z
print(rawget(y, 'foo')) -- nil
Mata methods 5/10
72
73. __call
__call可將資料表用作函數,稱為functable
__call具有可變數量的參數,第⼀個參數為資料表本身;隨後是呼叫
functable時實際的任意數量的參數
tbl = {
__call = function(table, val1, val2)
return 'Hello, from functor: ' .. (val1 + val2)
end
}
setmetatable(tbl, tbl)
message = tbl(2, 3); -- Calling the table like a function!
print('message: ' .. message) -- message: Hello, from functor: 5
Mata methods 5/10
73
75. __len:對應#運算子,預期回傳table的鍵-值對數量
employee1 = {name = 'tony', age = 25}
print (#employee1) -- 0
meta_tbl = {
__len = function(mytable)
len = 0
for k, v in pairs(mytable) do
len = len + 1
end
return len
end
}
setmetatable(employee1, meta_tbl)
print (#employee1) -- 2
Mata methods 7/10
75
76. __tostring:預期回傳table的內容(字串形式)
employee1 = {name = 'tony', age = 25}
print (employee1) -- table: 00000000006f9bd0
meta_tbl = {
__tostring = function(mytable)
str = '{'
for k, v in pairs(mytable) do
str = str .. k .. ':' .. v .. ', '
end
str = str .. '}'
return str
end
}
setmetatable(employee1, meta_tbl)
print (employee1) -- {age:25, name:tony, }
Mata methods 8/10
76
77. __concat:對應..運算子,預期將串接兩個table內容
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
__concat = function(mytable, newtable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, table_maxn(mytable)+1,newtable[i])
end
return mytable
end
})
Mata methods 9/10
77
78. secondtable = {4, 5, 6}
mytable = mytable .. secondtable
for k,v in ipairs(mytable) do
print(k,v)
end
Mata methods 10/10
78
83. math.randomseed(os.time())
print ('Guess a number between 10 and 100')
number = math.random(10, 100)
-- print ('Random: ' .. number)
repeat
local guess = tonumber( io.read() )
if guess ~= nil then
if guess == number then
print ('You guessed the number.')
break
elseif guess < number then
print ('Too low, guess again.')
else
print ('Too high, guess again.')
end
end
until false
Lua內建數學函式 5/7
83
88. filehandle:write('text')
寫入資料到檔案
file = io.open('data.txt', 'w')
file:write('foo', 'n')
file:write('barn')
-- Create function to save character data
function SaveCharacterData(name, power, team)
file:write('name ' .. name .. 'n') -- We can concatenate with ..
file:write('attack ', power, 'n') -- or use a comma seperated list
file:write('team ' .. team, 'n') -- we can even mix & match!
end
-- Call the function
SaveCharacterData('gwen', 20, 'blue')
檔案作業 2/6
88
91. hero = {}
f = io.open('data.txt')
f:read() -- Reads to end of line
f:read() -- Reads to end of line
f:read(5) -- read in 'name ' and discard it
hero.name = f:read() -- Reads to end of line, store name
f:read(7) -- read in 'attack ' and discard it
hero.health = f:read('*n') -- read the next number
f:read(6) -- read in 'team ' and discard it
hero.team = f:read('*l')-- Same as reading in the name
print ('hero')
print ('tname: ' .. hero.name )
print ('thealth: ' .. hero.health)
print ('tteam: ' .. hero.team)
檔案作業 5/6
91
92. filehandle:close()
關閉檔案
local file = io.open('data.txt', 'w')
file:write('foo', 'n')
file:write('bar')
file:close() -- THIS IS NEW! Don't forget to close!
os.rename('new_file.txt', 'renamed_file.txt')
變更檔名
os.remove('renamed_file.txt')
刪除檔案
os.execute('mkdir new_folder')
建立目錄
檔案作業 6/6
92
94. string.find(str1, str2)
在str1中尋找str2子字串,回傳子字串起始位置索引,找不到時回傳
nil
string.sub(str, start, end)
回傳從start到end之子字串,省略end參數則預設到字串結尾
local sentence = 'The quick brown fox'
local word = 'quick'
local start = string.find(sentence, word)
print ('substring found at index: ' .. start) -- 5
start = start + #word + 1
local result = string.sub(sentence, start)
print (result) -- brown fox
字串處理作業 1/2
94
95. string.upper(str)
將字串轉換為大寫字⺟
string.lower(str)
將字串轉換為大寫字⺟
string函式庫支援使用:運算元,先前程式可改寫如下:
local sentence = 'The quick brown fox'
local word = 'quick'
local start = sentence:find(word)
print ('substring found at index: ' .. start) -- 5
start = start + #word + 1
local result = sentence:sub(start)
print (result) -- brown fox
字串處理作業 2/2
95
97. -- module_character.lua
local character = {} -- It's important that the table retuned be local!
character.health = 20
character.strength = 5
character.name = ''
character.new = function (self, object)
object = object or {} -- Use provided table, or create new one
local provided = ''
if type(object) == 'string' then
provided = object
object = {}
end
setmetatable(object, self) -- Assign meta table
self.__index = self
if provided ~= '' then
object.name = provided
end
return object
end
使用模組 2/6
97
98. character.attack = function(self, other)
print (self.name .. ' attacks ' .. other.name)
other.health = other.health - self.strength
if other.health < 1 then
print ('t' .. other.name .. ' is dead')
else
print ('t' .. other.name .. ' has ' .. other.health .. ' health left')
end
end
return character
使用模組 3/6
98
99. -- module_main.lua
-- load the character module into a table named character
Character = require ('module_character')
-- Create a new hero, which is a charcter
gwen = Character:new('gwen')
gwen.strength = 10
-- Create a new enemy, also a character
orc = Character:new('orc')
-- Run game logic
gwen:attack(orc)
orc:attack(gwen)
gwen:attack(orc)
使用模組 4/6
99
102. Lua並未支援原生的物件導向程式設計類別(class)機制,但運用
meta table也可以實現類似功能
Enemy = { }
Enemy.health = 200
Enemy.attack = 4
Enemy.defense = 20
Enemy.new = function (self, object)
object = object or {} -- Use provided table, or create new one
setmetatable(object, self) -- Assign meta table
self.__index = self
return object
end
Enemy.hit = function(self, damage)
damage = damage - self.defense
if damage < 0 then
damage = 0
end
self.health = self.health - damage
end
物件導向程式設計 1/13
102
103. grunt = Enemy.new(Enemy) -- Health is stored in 'Enemy'
miniBoss = Enemy.new(Enemy) -- Health is stored in 'Enemy'
boss = Enemy.new(Enemy, {health = 500, defense = 100})
miniBoss.health = 250 -- Health is now stored in 'miniBoss'
-- grunt does not have a health variable, so the enemy table health is returned
print ('grunt health: ' .. grunt.health) -- 200
-- miniBoss has a health variable, it was created in the above assignment
print ('mini boss health: ' .. miniBoss.health) --250
-- boss also has a health variable, so the boss table health is returned
print ('boss health: ' .. boss.health) -- 500
print ('Hero attacks both boss and grunt')
Enemy.hit(boss, 50)
Enemy.hit(grunt, 55)
print ('grunt health: ' .. grunt.health) -- 165
print ('boss health: ' .. boss.health) -- 500
物件導向程式設計 2/13
103
109. 解決方式是在設置物件的meta table之前為每個實例配置各自的table
Character = {
alive = true
}
Character.position = {
x = 10, y = 20, z = 30
}
Character.new = function(self, object)
object = object or {}
-- Assign per instance variables after the object is valid but before
-- setting the meta table! Copy all members of the new table by value!
object.position = {}
object.position.x = Character.position.x
object.position.y = Character.position.y
object.position.z = Character.position.z
setmetatable(object, self)
self.__index = self
return object
end
物件導向程式設計 8/13
109
110. player1 = Character:new()
player2 = Character:new()
player1.position.x = 0
player2.position.y = 10
print ('Player 1, position: (' .. player1.position.x .. ', '
.. player1.position.y .. ', ' .. player1.position.z .. ')')
print ('Player 2, position: (' .. player2.position.x .. ', '
.. player2.position.y .. ', ' .. player2.position.z .. ')')
print ('Table id:')
print ('Player 1: ' .. tostring(player1.position))
print ('Player 2 :' .. tostring(player2.position))
if player1.position == player2.positon then
print ('Player 1 and 2 have the same position reference');
else
print ('Player 1 and 2 have unique positon tables');
end
物件導向程式設計 9/13
110
112. 繼承性 (inheritance)
Animal = {
sound = ''
}
Animal.new = function(self, object)
object = object or {}
setmetatable(object, self)
self.__index = self
return object
end
Animal.MakeSound = function(self)
print(self.sound)
end
-- Dog is a class, not an object (instance)
Dog = Animal:new()
Dog.sound = 'woof'
物件導向程式設計 11/13
112
113. -- Cat is a class, not an Object (instance)
Cat = Animal:new()
Cat.sound = 'meow'
Cat.angry = false
Cat.MakeSound = function(self)
if self.angry then
print('hissss')
else
print(self.sound)
end
end
animals = { Cat:new(), Dog:new(), Cat:new() }
animals[1].angry = true
for i,v in ipairs(animals) do
-- The current animal is stored in the v variable. It doesn't matter if
-- the animal is a Dog or a Cat Both Dog and Cat extend Animal, which is
-- guaranteed to contain a MakeSound function.
v:MakeSound()
end
物件導向程式設計 12/13
113