读书笔记: Programming in Lua, 4th Edition.
Account = {
balance = 0,
withdraw = function(self, v)
self.balance = self.balance - v
end
}
function Account:deposit(v)
self.balance = self.balance + v
end
-- 用.来调用函数的话,需要手动给self传值。
-- 用冒号来调用函数的话,可以省略self。
Account.deposit(Account, 200)
Account:deposit(200)
Account.withdraw(Account, 100)
Account:withdraw(100)
在上面的例子中,Account只是一个对象实例。
在C++中,一个类,可以生成多个对象实例。
在Lua中,没有类的概念,Lua是用metatable来模拟实现类的。
if we have two objects A and B, all we have to do to make B a prototype for A is this:
setmetatable(A, {__index = B})
After that, A looks up in B for any operation that it does not have.
Let us go back to our example of a bank account. To create other accounts with behavior similar to Account, we arrange for these new objects to inherit their operations from Account, using the __index metamethod.
local mt = {__index = Account}
function Account.new (o)
o = o or {} -- create table if user does not provide one
setmetatable(o, mt)
return o
end
After this code, what happens when we create a new account and call a method on it, like this?
a = Account.new{balance = 0}
a:deposit(100.00)
When we create the new account, a, it will have mt as its metatable. When we call a:deposit(100.00), we are actually calling a.deposit(a, 100.00); the colon is only syntactic sugar. However, Lua cannot find a “deposit” entry in the table a; hence, Lua looks into the __index entry of the metatable. The situation now is more or less like this:
getmetatable(a).__index.deposit(a, 100.00)
The metatable of a is mt, and mt.__index is Account. Therefore, the previous expression evaluates to this one:
Account.deposit(a, 100.00)
That is, Lua calls the original deposit function, but passing a as the self parameter. So, the new account a inherited the function deposit from Account. By the same mechanism, it inherits all fields from Account.
We can make two small improvements on this scheme. The first one is that we do not need to create a new table for the metatable role; instead, we can use the Account table itself for that purpose. The second one is that we can use the colon syntax for the new method, too. With these two changes, method new becomes like this:
function Account:new (o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
Now, when we call Account:new(), the hidden parameter self gets Account as its value, we make Account.__index also equal to Account, and set Account as the metatable for the new object. It may seem that we do not gained much with the second change (the colon syntax); the advantage of using self will become apparent when we introduce class inheritance, in the next section.
Account = {balance = 0}
function Account:new(o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
function Account:deposit(v)
self.balance = self.balance + v
end
function Account:withdraw(v)
if v > self.balance then error"insufficient funds" end
self.balance = self.balance - v
end
SpecialAccount = Account:new()
s = SpecialAccount:new{limit = 1000.00}
function SpecialAccount:withdraw(v)
if v - self.balance >= self.getLimit() then
error"insufficient funds"
end
self.balance = self.balance - v
end
function SpecialAccount:getLimit()
return self.limit or 0
end
就像一个链表一样,通过metatable一层一层向上找。
Lua中的变量,如果没有local关键字,全都是全局变量,Lua也是用Table来管理全局变量的,Lua把这些全局变量放在了一个叫“_G”的Table里。
> a = 1
> print(a)
1
> _G["a"]
1
> a = 2
> _G["a"]
2
打印出全局变量_G中的所有东西:
for n in pairs(_G) do
print(n)
end
读书笔记: Programming in Lua, 4th Edition.
do-end 就像是一个花括号,用来构成一个作用域。
> local x1
> do
>> local x1 = 1
>> print(x1)
>> end
1
> print(x1)
nil
if op == "+" then
r = a + b
elseif op == "-" then
r = a - b
elseif op == "*" then
r = a * b
elseif op == "/" then
r = a / b
else
error("invalid operation")
end
local i = 1
while a[i] do
print(a[i])
i = i + 1
end
至少执行一次,就是C语言中的do-while
-- print the first non-empty input line
local line
repeat
line = io.read()
until line ~= ""
print(line)
Differently from most other languages, in Lua the scope of a local variable declared inside the loop includes the condition:
-- computes the square root of 'x' using Newton-Raphson method
local sqr = x / 2
repeat
sqr = (sqr + x/sqr) / 2
local error = math.abs(sqr^2 - x)
until error < x/10000 -- local 'error' still visible here
The for statement has two variants: the numerical for and the generic for.
This loop will execute something for each value of var from exp1 to exp2, using exp3 as the step to increment var. This third expression is optional; when absent, Lua assumes one as the step value.
for var = exp1, exp2, exp3 do
something
end
If we want a loop without an upper limit, we can use the constant math.huge:
for i = 1, math.huge do
if (0.3*i^3 - 20*i^2 - 500 >= 0) then
print(i)
break
end
end
Creates a new coroutine, with body f. f must be a function. Returns this new coroutine, an object with type “thread”.
创建一个新的协程。f必须是一个函数。返回值是这个新的协程,返回值的类型是thread。
Returns true when the running coroutine can yield.
A running coroutine is yieldable if it is not the main thread and it is not inside a non-yieldable C function.
返回true当正在运行的协程可以被yield。
Starts or continues the execution of coroutine co. The first time you resume a coroutine, it starts running its body. The values val1, … are passed as the arguments to the body function. If the coroutine has yielded, resume restarts it; the values val1, … are passed as the results from the yield.
If the coroutine runs without any errors, resume returns true plus any values passed to yield (when the coroutine yields) or any values returned by the body function (when the coroutine terminates). If there is any error, resume returns false plus the error message.
Returns the running coroutine plus a boolean, true when the running coroutine is the main one.
返回一个正在运行的协程和一个boolean值,这个协程是主线程的话boolean值为true。
Returns the status of coroutine co, as a string: “running”, if the coroutine is running (that is, it called status); “suspended”, if the coroutine is suspended in a call to yield, or if it has not started running yet; “normal” if the coroutine is active but not running (that is, it has resumed another coroutine); and “dead” if the coroutine has finished its body function, or if it has stopped with an error.
Creates a new coroutine, with body f. f must be a function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error.
Suspends the execution of the calling coroutine. Any arguments to yield are passed as extra results to resume.
挂起调用这个函数的协程。参数会传递给resume,作为resume的返回值。
co = coroutine.create(
function(i)
print(i);
end
)
print(coroutine.status( co ))
coroutine.resume(co, 1)
print(coroutine.status( co ))
print("---------------")
co = coroutine.wrap(
function(i)
print(i);
end
)
co(2)
print("---------------")
co2 = coroutine.create(
function()
for i = 1, 10 do
print(i)
if i == 3 then
print(coroutine.status(co2))
print(coroutine.running( ))
end
coroutine.yield()
end
end
)
coroutine.resume(co2)
coroutine.resume(co2)
coroutine.resume(co2)
print(coroutine.status(co2))
print(coroutine.running( ))
输出:
suspended
1
dead
---------------
2
---------------
1
2
3
running
thread: 0000000000459dd8 false
suspended
thread: 0000000000456638 true
function foo(a)
print("foo a = ", a)
return coroutine.yield(2 * a)
end
co = coroutine.create(
function(a, b)
print("1==== ", a, b)
local r = foo( a + 1 )
print("2==== ", r)
local r, s = coroutine.yield(a + b, a - b)
print("3==== ", r, s)
return b, "end of coroutine."
end
)
print("main", coroutine.resume(co, 1, 10))
print("--------------")
print("main", coroutine.resume(co, "abc"))
print("--------------")
print("main", coroutine.resume(co, "xxx", "yyy"))
print("--------------")
print("main", coroutine.resume(co, "xxx", "yyy"))
print("--------------")
输出:
1==== 1 10
foo a = 2
main true 4
--------------
2==== abc
main true 11 -9
--------------
3==== xxx yyy
main true 10 end of coroutine.
--------------
main false cannot resume dead coroutine
--------------
local newProductor
function productor()
local i = 0
while true do
i = i + 1
if i > 10 then
break;
end
send(i)
end
end
function consumer()
while true do
local s, i = receive()
if s == false then
break;
end
print(i)
end
end
function receive()
local status, value = coroutine.resume(newProductor)
return status, value
end
function send(x)
coroutine.yield(x)
end
newProductor = coroutine.create( productor )
consumer()
输出:
1
2
3
4
5
6
7
8
9
10
nil
读书笔记: Programming in Lua, 4th Edition.
> a = {}
> a["x"] = 10
> b = a -- 'b' refers to the same table as 'a'
> b["x"] --> 10
> b["x"] = 20
> a["x"] --> 20
> a = nil -- only 'b' still refers to the table
> b = nil -- no references left to the table
When a program has no more references to a table, the garbage collector will eventually delete the table and reuse its memory.
> a = {} -- empty table
> a.x = 10 -- same as a["x"] = 10
> a.x --> 10 -- same as a["x"]
> a.y --> nil -- same as a["y"]
A common mistake for beginners is to confuse a.x with a[x]. The first form represents a[“x”], that is, a table indexed by the string “x”. The second form is a table indexed by the value of the variable x. See the difference:
> a = {}
> x = "y"
> a[x] = 10 -- put 10 in field "y"
> a[x] --> 10 -- value of field "y"
> a.x --> nil -- value of field "x" (undefined)
> a.y --> 10 -- value of field "y"
> i = 10; j = "10"; k = "+10"
> a = {}
> a[i] = "number key"
> a[j] = "string key"
> a[k] = "another string key"
> a[i] --> number key
> a[j] --> string key
> a[k] --> another string key
> a[tonumber(j)] --> number key
> a[tonumber(k)] --> number key
days = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
print(days[4]) --> Wednesday
opnames = {["+"] = "add", ["-"] = "sub",
["*"] = "mul", ["/"] = "div"}
i = 20; s = "-"
a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}
print(opnames[s]) --> sub
print(a[22]) --> ---
{x = 0, y = 0} <--> {["x"] = 0, ["y"] = 0}
{"r", "g", "b"} <--> {[1] = "r", [2] = "g", [3] = "b"}
To represent a conventional array or a list, we simply use a table with integer keys.
We call such a list without holes a sequence.
We can traverse all key–value pairs in a table with the pairs iterator:
t = {10, print, x = 12, k = "hi"}
for k, v in pairs(t) do
print(k, v)
end
--> 1 10
--> k hi
--> 2 function: 0x420610
--> x 12
For lists, we can use the ipairs iterator:
t = {10, print, 12, "hi"}
for k, v in ipairs(t) do
print(k, v)
end
--> 1 10
--> 2 function: 0x420610
--> 3 12
--> 4 hi
In this case, Lua trivially ensures the order.
Another way to traverse a sequence is with a numerical for:
t = {10, print, 12, "hi"}
for k = 1, #t do
print(k, t[k])
end
--> 1 10
--> 2 function: 0x420610
--> 3 12
--> 4 hi
读书笔记: Programming in Lua, 4th Edition.
> a = "one string"
> b = string.gsub(a, "one", "another")
> print(a, b)
one string another string
>
> a = "hello"
> print(#a)
5
> print(#"good bye")
8
> "Hello " .. "World"
Hello World
> "result is " .. 3
result is 3
> a = "Hello"
> a .. " World"
Hello World
a = "a line"
b = 'another line'
They are equivalent; the only difference is that inside each kind of quote we can use the other quote without escapes.
> page = [[
>> <html>
>> <head>
>> <title>An HTML Page</title>
>> </head>
>> <body>
>> <a href="http://www.lua.org">Lua</a>
>> </body>
>> </html>
>> ]]
> print(page)
<html>
<head>
<title>An HTML Page</title>
</head>
<body>
<a href="http://www.lua.org">Lua</a>
</body>
</html>
> print(10 .. 20)
1020
> "10" + 1
11.0
> tonumber(" -3 ")
-3
> tonumber(" 10e4 ")
100000.0
> tonumber("10e")
nil
By default, tonumber assumes decimal notation, but we can specify any base between 2 and 36 for the conversion
> tonumber("100101", 2)
37
> tonumber("fff", 16)
4095
> tonumber("-ZZ", 36)
-1295
> tonumber("987", 8)
nil
> print(tostring(10) == "10")
true
> string.len("abc")
3
> string.rep("abc", 3)
abcabcabc
> string.reverse("A Long Line!")
!eniL gnoL A
> string.lower("A Long Line!")
a long line!
> string.upper("A Long Line!")
A LONG LINE!
As a typical use, if we want to compare two strings regardless of case, we can write something like this:
string.lower(a) < string.lower(b)
The call string.sub(s, i, j) extracts a piece of the string s, from the i-th to the j-th character inclusive. (The first character of a string has index 1.) We can also use negative indices, which count from the end of the string: index -1 refers to the last character, -2 to the previous one, and so on. Therefore, the call string.sub(s, 1, j) gets a prefix of the string s with length j; string.sub(s, j, -1) gets a suffix of the string, starting at the j-th character; and string.sub(s, 2, -2) returns a copy of the string s with the first and last characters removed:
> s = "[in brackets]"
> string.sub(s, 2, -2)
in brackets
> string.sub(s, 1, 3)
[in
> string.sub(s, -1, -1)
]
> string.sub(s, -2, -1)
s]
> print(s)
[in brackets]
> s = string.sub(s, 2, -2)
> print(s)
in brackets
> print(string.char(97))
a
> i = 99; print(string.char(i, i+1, i+2))
cde
> print(string.byte("abc"))
97
> print(string.byte("abc", 2))
98
> print(string.byte("abc", -1))
99
A call like string.byte(s, i, j) returns multiple values with the numeric representation of all characters between indices i and j (inclusive):
> print(string.byte("abc", 1, 2))
97 98
A nice idiom is {string.byte(s, 1, -1)}, which creates a list with the codes of all characters in s.
和C语言的printf是一样的。
> string.format("x = %d y = %d", 10, 20)
x = 10 y = 20
> string.format("x = %x", 200)
x = c8
> string.format("x = 0x%X", 200)
x = 0xC8
> string.format("x = %f", 200)
x = 200.000000
> tag, title = "h1", "a title"
> string.format("<%s>%s<%s>", tag, title, tag)
<h1>a title<h1>
> print(string.format("pi = %.4f", math.pi))
pi = 3.1416
> d = 5; m = 11; y = 1990
> print(string.format("%02d/%02d/%04d", d, m, y))
05/11/1990
> string.find("hello world", "wor")
7 9
> string.find("hello world", "war")
nil
> string.gsub("hello world", "l", ".")
he..o wor.d 3
> string.gsub("hello world", "ll", "..")
he..o world 1
> string.gsub("hello world", "a", ".")
hello world 0