本篇文章作為Lua基礎部分的一個小結,演示兩個小程序,來表現Lua的不同特性。第一個例子說明Lua如何作為一門數據描述性語言使用。第2個例子,是一個馬爾可夫鏈算法的實現。
**ps**:個人覺得書中的這一章有點莫名其妙,感覺兩個例子也沒有起到什么總結作用,反而感覺講得有點云里霧里的。
## 1. 數據描述
在Lua的網站上保留了一個數據庫,存儲了世界上使用Lua的項目的一些示例代碼。我們用一個結構體來表示數據庫中的每一個條目,如下所示:
~~~
entry{
title = "Tecgraf",
org = "Computer Graphics Technology Group, PUC-Rio",
url = "http://www.tecgraf.puc-rio.br/",
contact = "Waldemar Celes",
description = [[
Tecgraf is the result of a partnership between PUC-Rio,
the Pontifical Catholic University of Rio de Janeiro,
and <a HREF="http://www.petrobras.com.br/">PETROBRAS</a>,
the Brazilian Oil Company.
Tecgraf is Lua's birthplace,
and the language has been used there since 1993.
Currently, more than thirty programmers in Tecgraf use
Lua regularly; they have written more than two hundred
thousand lines of code, distributed among dozens of
final products.]]
}
~~~
含有一系列這樣條目的一個數據文件,居然也是一個Lua程序,它以table為參數去對函數*entry* 進行一系列調用。
我們要寫一個程序將這些數據以HTML格式展示出來,這些數據就變成網頁 http://www.lua.org/uses.html。 因為有很多項目,最終的頁面先列出所有項目的主題,再展示每個項目的細節。如下所示,是程序的一個典型輸出:
~~~
<html>
<head><title>Projects using Lua</title></head>
<body bgcolor="#FFFFFF">
Here are brief descriptions of some projects around the
world that use <a href="home.html">Lua</a>.
<br>
<ul>
<li><a href="#1">Tecgraf</a>
<li> <other entries>
</ul>
<h3>
<a name="1" href="http://www.tecgraf.puc-rio.br/">Tecgraf</a>
<br>
<small><em>Computer Graphics Technology Group,
PUC-Rio</em></small>
</h3>
Tecgraf is the result of a partnership between
...
distributed among dozens of final products.<p>
Contact: Waldemar Celes
<a name="2"></a><hr>
<other entries>
</body></html>
~~~
為了讀取數據,程序簡單定義了*entry* ,然后用*dofile* 運行該數據文件。注意,我們必須遍歷所有的條目兩遍,第一次是為了獲取主題列表,第二次來獲取項目描述信息。一種方法是將所有的條目手收集到一個array中。但是,還有另一個比較吸引人的方法:運行這個數據文件兩次,每次使用不同的*entry* 定義。下面我們使用第二種方法。
首先,我們定義一個格式化寫入的函數:
~~~
function fwrite (fmt, ...)
return io.write(string.format(fmt, ...))
end
~~~
函數*writeheader* 簡單的寫入page header,如下:
~~~
function writeheader()
io.write([[
<html>
<head><title>Projects using Lua</title></head>
<body bgcolor="#FFFFFF">
Here are brief descriptions of some projects around the
world that use <a href="home.html">Lua</a>.
<br>
]])
end
~~~
*entry* 的第一個定義,將每一個項目主題寫入到list中為一個條目,參數*o*? 是描述項目的table:
~~~
function entry1 (o)
count = count + 1
local title = o.title or '(no title)'
fwrite('<li><a href="#%d">%s</a>\n', count, title)
end
~~~
如果*o.title* 為**nil**(也就是說這個域沒有被提供),函數使用一個固定的"(no title)"。
*entry* 的第二個定義如下,寫入一個項目的所有有用數據。有一點復雜,因為所有的選項都是可選的。(HTML中使用雙引號,為了避免跟HTML沖突,我們在程序中使用單引號)。
~~~
function entry2 (o)
count = count + 1
fwrite('<hr>\n<h3>\n')
local href = o.url and string.format(' href="%s"', o.url) or ''
local title = o.title or o.org or 'org'
fwrite('<a name="%d"%s>%s</a>\n', count, href, title)
if o.title and o.org then
fwrite('<br>\n<small><em>%s</em></small>', o.org)
end
fwrite('\n</h3>\n')
if o.description then
fwrite('%s<p>\n',
string.gsub(o.description, '\n\n+', '<p>\n'))
end
if o.email then
fwrite('Contact: <a href="mailto:%s">%s</a>\n',
o.email, o.contact or o.email)
elseif o.contact then
fwrite('Contact: %s\n', o.contact)
end
end
~~~
最后一個函數*writetail* ,寫page tail。
~~~
function writetail ()
fwrite('</body></html>\n')
end
~~~
主程序如下所示。程序打開頁面,加載數據文件,用*entry* 的第一個定義(entry1)來創建主題列表,然后重置計數器,再用*entry* 的第二個定義(entry2)來運行數據文件,最后關閉頁面。
~~~
local inputfile = 'db.lua'
writeheader()
count = 0
f = loadfile(inputfile) -- loads data file
entry = entry1 -- defines 'entry'
fwrite('<ul>\n')
f() -- runs data file
fwrite('</ul>\n')
count = 0
entry = entry2 -- redefines 'entry'
f() -- runs data file again
writetail()
~~~
匯總了一下上面的程序代碼如下:
~~~
function fwrite (fmt, ...)
return io.write(string.format(fmt, ...))
end
function writeheader()
io.write([[
<html>
<head><title>Projects using lua</title></head>
<body bgcolor="#FFFFFF">
Here are brief description of some projects around the world
that use <a href="home.html">Lua</a>.
<br>
]])
end
function entry1 (o)
count = count + 1
local title = o.title or '(no title)'
fwrite('<li><a href="#%d">%s</a>\n', count, title)
end
function entry2 (o)
count = count + 1
fwrite('<hr>\n<h3>\n')
local href = o.url and string.format(' href="%s"', o.url)
local title = o.title or o.org or 'org'
fwrite('<a name="%d"%s>%s</a>\n', count, href, title)
if o.title and o.org then
fwrite('<br>\n<small><em>%s</em></small>', o.org)
end
fwrite('\n</h3>\n')
if o.description then
fwrite('%s<p>\n',
string.gsub(o.description, '\n\n+', '<p>\n'))
end
if o.email then
fwrite('Contact: <a href="mailto:%s">%s</a>\n',
o.email, o.contact or o.email)
elseif o.contact then
fwrite('Contact: %s\n', o.contact)
end
end
function writetail ()
fwrite('</body></html>\n')
end
local inputfile = 'db.lua'
writeheader()
count = 0
f = loadfile(inputfile) -- loads data file
entry = entry1 -- defines 'entry'
fwrite('<ul>\n')
f() -- runs data file
fwrite('</ul>\n')
count = 0
entry = entry2 -- redefines 'entry'
f() -- runs data file again
writetail()
~~~
db.lua文件的內容如下:
~~~
entry{
title = "Tecgraf",
org = "Computer Graphics Technology Group, PUC-Rio",
url = "http://www.tecgraf.puc-rio.br/",
contact = "Waldemar Celes",
description = [[
TeCGraf is the result of a partnership between PUC-Rio,
the Pontifical Catholic University of Rio de Janeiro,
and <A HREF="http://www.petrobras.com.br/">PETROBRAS</A>,
the Brazilian Oil Company.
TeCGraf is Lua's birthplace,
and the language has been used there since 1993.
Currently, more than thirty programmers in TeCGraf use
Lua regularly; they have written more than two hundred
thousand lines of code, distributed among dozens of
final products.]]
}
entry{
title = "Tecgraf_02",
org = "Computer Graphics Technology Group, PUC-Rio, the 2nd entry",
url = "http://www.tecgraf.puc-rio.br/, the 2nd entry",
contact = "Waldemar Celes 02",
description = [[
This is the 2nd entry,
TeCGraf is the result of a partnership between PUC-Rio,
the Pontifical Catholic University of Rio de Janeiro,
and <A HREF="http://www.petrobras.com.br/">PETROBRAS</A>,
the Brazilian Oil Company.
TeCGraf is Lua's birthplace,
and the language has been used there since 1993.
Currently, more than thirty programmers in TeCGraf use
Lua regularly; they have written more than two hundred
thousand lines of code, distributed among dozens of
final products.]]
}
~~~
運行結果如下:

## 2. 馬爾可夫鏈算法
第2個例子是馬爾可夫鏈算法的實現.這個程序基于文本中的前n個詞來生成隨機的文本,這里我們假設n為2。
程序的第一部分,讀取基本文本,并創建一個table,每兩個單詞為一個前綴,將基本文本中在該前綴之后的單詞(可能有多個)存入table中。創建完該table后,程序用這個table去隨機生成文本,每個前綴后的單詞出現的概率跟在基本文本中大致相同。這樣,我們就得到一個相當隨機的文本。
我們會把兩個單詞用一個空格“ ”鏈接起來,編碼為前綴:
~~~
function prefix (w1, w2)
return w1 .. " " .. w2
end
~~~
我們使用字符串 NOWORD("\n")來初始化前綴單詞,并且標記文本的結尾。例如:
~~~
the more we try the more we do
~~~
生成的table將會是:
~~~
{ ["\n \n"] = {"the"},
["\n the"] = {"more"},
["the more"] = {"we", "we"}, -- 有兩處"the more we"
["more we"] = {"try", "do"}, -- 兩處"more we try", "more we do"
["we try"] = {"the"},
["try the"] = {"more"},
["we do"] = {"\n"},
}
~~~
程序將它的table保存到變量statetab中。我們用下面的函數在這個table的前綴list中插入一個新的單詞
~~~
function insert (index, value)
local list = statetab[index]
if list == nil then
statetab[index] = {value}
else
list[#list + 1] = value
end
end
~~~
它首先檢查這個前綴是否有list了;如果么有,那么用這個新值創建一個新的list;否則,就將這個新值插入到已存在的list的末尾。
要創建statetab這個table,我們保存兩個變量,w1 和 w2,保存最后讀取的兩個單詞。每讀取一個新的單詞,我們就將它添加到與w1-w2關聯的list中,然后update一下w1和w2。
創建完這個table后,程序開始用MAXGEN個單詞來生成文本。首先,它重新初始化w1和w2。然后,對每個前綴,它從合法的下一個單詞的list中隨機選取一個,打印這個單詞,然后update下w1和w2. ? 下面是完整版的程序。
~~~
-- Auxiliary definitions for the Markov program
function allwords ()
local line = io.read() -- current line
local pos = 1 -- current position in the line
return function () -- iterator function
while line do -- repeat while there are lines
local s, e = string.find(line, "%w+", pos)
if s then -- found a word?
pos = e + 1 -- update next position
return string.sub(line, s, e) -- return the word
else
line = io.read() -- word not found; try next line
pos = 1 -- restart from first position
end
end
return nil -- no more lines: end of traversal
end
end
function prefix (w1, w2)
return w1 .. " " .. w2
end
local statetab = {}
function insert (index, value)
local list = statetab[index]
if list == nil then
statetab[index] = {value}
else
list[#list + 1] = value
end
end
-- The Markov program
local N = 2
local MAXGEN = 10000
local NOWORD = "\n"
-- build table
local w1, w2 = NOWORD, NOWORD
for w in allwords() do
insert(prefix(w1, w2), w)
w1 = w2; w2 = w;
end
insert(prefix(w1, w2), NOWORD)
-- generate text
w1 = NOWORD; w2 = NOWORD -- reinitialize
for i=1, MAXGEN do
local list = statetab[prefix(w1, w2)]
-- choose a random item from list
local r = math.random(#list)
local nextword = list[r]
if nextword == NOWORD then return end
io.write(nextword, " ")
w1 = w2; w2 = nextword
end
~~~
運行結果如下:

水平有限,如果有朋友發現錯誤,歡迎留言交流