Chapter 10: Complete Examples
最后,我们以两个展示Lua 的不同特性的完整示例程序来结束关于语言的介绍。第一个示例展示了将Lua 作为一种数据描述语言来使用。第二个例子是一个马尔可夫链的实现,由Kernighan & Pike 的《Practice of Programming》 (Addison-Wesley, 1999) 一书描述。
10.1 Data Description
生成HTML
--[=[
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.]]
}
--]=]
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 descriptions 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 writetail ()
fwrite('</body></html>/n')
end
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
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()
10.2 Markov Chain Algorithm
我们的第二个例子是马尔可夫链算法的实现。程序生成随机文本,规则是基于原文中什么单词跟在n 个连续的单词后面。这个例子我们假定n 的值是2。
程序的第一部分读取原文,然后基于这个文本创建一个表,对每个两单词前缀,给定一个单词的列表,它们是跟在前缀后面的单词。建完表后,程序使用这个表来生成随机文本,每个单词跟在两个前缀单词后面,并且它们在文本中等概率出现。结果是,我们得到非常,但不完全随机的文本。
我们将前缀编码为两个单词中间连接一个空格:
function prefix (w1, w2)
return w1 .. " " .. w2
end
我们使用NOWORD (“/n”) 作为初始前缀,并且也用它来标记文本的结束(生成的文本最后一个单词是“/n”),例如下面的文本:
the more we try the more we do
对其建表得到:
{ ["/n /n"] = {"the"},
["/n the"] = {"more"},
["the more"] = {"we", "we"},
["more we"] = {"try", "do"},
["we try"] = {"the"},
["try the"] = {"more"},
["we do"] = {"/n"},
}
因为原文中的"the" 没有前缀,所以用"/n /n" 作为初始前缀。这一项后面["try the"] = {"more"}
为什么不是["the more "] = {"we"} ?因为前缀"the more " 前面已经出现过了。同理,"more we" 也出现过了,所以下一项直接是"we do",后面已经没有文本,所以后面接"/n" 作为结束。
程序用变量statetab 来保存前缀表。在前缀表中插入新单词,我们使用下面的函数:
function insert (index, value)
local list = statetab[index]
if list == nil then
statetab[index] = {value}
else
list[#list + 1] = value
end
end
马尔可夫链算法
function allwords (f)
local line = f:read("*line") -- current line
local pos = 1 -- current position in the line
return function ()
while line do
local s, e = string.find(line, "%w+", pos)
if s then -- found a word?
pos = e + 1
return string.sub(line, s, e)
else
line = f:read("*line")
pos = 1
end
end
return nil
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
f = io.open("c://input.txt","r") -- open input file
assert(f)
local N = 2
local MAXGEN = 10000
local NOWORD = "/n"
-- build table
local w1, w2 = NOWORD, NOWORD
for w in allwords(f) 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) -- Bug,这里的随机数是不随机的。
local nextword = list[r]
if nextword == NOWORD then return end
io.write(nextword, " ")
w1 = w2; w2 = nextword
end
f:close()