小结(两个Lua程序示例)

最后更新于:2022-04-01 20:44:22

本篇文章作为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 PETROBRAS, 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。 因为有很多项目,最终的页面先列出所有项目的主题,再展示每个项目的细节。如下所示,是程序的一个典型输出: ~~~ Projects using Lua Here are brief descriptions of some projects around the world that use Lua.

Tecgraf
Computer Graphics Technology Group, PUC-Rio

Tecgraf is the result of a partnership between ... distributed among dozens of final products.

Contact: Waldemar Celes


~~~ 为了读取数据,程序简单定义了*entry* ,然后用*dofile* 运行该数据文件。注意,我们必须遍历所有的条目两遍,第一次是为了获取主题列表,第二次来获取项目描述信息。一种方法是将所有的条目手收集到一个array中。但是,还有另一个比较吸引人的方法:运行这个数据文件两次,每次使用不同的*entry* 定义。下面我们使用第二种方法。 首先,我们定义一个格式化写入的函数: ~~~ function fwrite (fmt, ...) return io.write(string.format(fmt, ...)) end ~~~ 函数*writeheader* 简单的写入page header,如下: ~~~ function writeheader() io.write([[ Projects using Lua Here are brief descriptions of some projects around the world that use Lua.
]]) end ~~~ *entry* 的第一个定义,将每一个项目主题写入到list中为一个条目,参数*o*  是描述项目的table: ~~~ function entry1 (o) count = count + 1 local title = o.title or '(no title)' fwrite('
  • %s\n', count, title) end ~~~ 如果*o.title* 为**nil**(也就是说这个域没有被提供),函数使用一个固定的"(no title)"。 *entry* 的第二个定义如下,写入一个项目的所有有用数据。有一点复杂,因为所有的选项都是可选的。(HTML中使用双引号,为了避免跟HTML冲突,我们在程序中使用单引号)。 ~~~ function entry2 (o) count = count + 1 fwrite('
    \n

    \n') local href = o.url and string.format(' href="%s"', o.url) or '' local title = o.title or o.org or 'org' fwrite('%s\n', count, href, title) if o.title and o.org then fwrite('
    \n%s', o.org) end fwrite('\n

    \n') if o.description then fwrite('%s

    \n', string.gsub(o.description, '\n\n+', '

    \n')) end if o.email then fwrite('Contact: %s\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('\n') end ~~~ 主程序如下所示。程序打开页面,加载数据文件,用*entry* 的第一个定义(entry1)来创建主题列表,然后重置计数器,再用*entry* 的第二个定义(entry2)来运行数据文件,最后关闭页面。 ~~~ local inputfile = 'db.lua' writeheader() count = 0 f = loadfile(inputfile) -- loads data file entry = entry1 -- defines 'entry' fwrite('

      \n') f() -- runs data file fwrite('
    \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([[ Projects using lua Here are brief description of some projects around the world that use Lua.
    ]]) end function entry1 (o) count = count + 1 local title = o.title or '(no title)' fwrite('
  • %s\n', count, title) end function entry2 (o) count = count + 1 fwrite('
    \n

    \n') local href = o.url and string.format(' href="%s"', o.url) local title = o.title or o.org or 'org' fwrite('%s\n', count, href, title) if o.title and o.org then fwrite('
    \n%s', o.org) end fwrite('\n

    \n') if o.description then fwrite('%s

    \n', string.gsub(o.description, '\n\n+', '

    \n')) end if o.email then fwrite('Contact: %s\n', o.email, o.contact or o.email) elseif o.contact then fwrite('Contact: %s\n', o.contact) end end function writetail () fwrite('\n') end local inputfile = 'db.lua' writeheader() count = 0 f = loadfile(inputfile) -- loads data file entry = entry1 -- defines 'entry' fwrite('

      \n') f() -- runs data file fwrite('
    \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 PETROBRAS, 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 PETROBRAS, 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.]] } ~~~ 运行结果如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5ef2a654a.jpg) ## 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 ~~~ 运行结果如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-09-06_57ce5ef2c7a07.jpg) 水平有限,如果有朋友发现错误,欢迎留言交流
  • ';