Lua基础 小结(两个Lua程序示例)

本篇文章作为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.titlenil (也就是说这个域没有被提供),函数使用一个固定的"(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.]]

      } 

 

运行结果如下:

Lua基础 小结(两个Lua程序示例)

 


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

 

运行结果如下:

Lua基础 小结(两个Lua程序示例)


 

水平有限,如果有朋友发现错误,欢迎留言交流


 

你可能感兴趣的:(lua)