《Lua程序设计第四版》 第二部分14~17章自做练习题答案

这篇具有很好参考价值的文章主要介绍了《Lua程序设计第四版》 第二部分14~17章自做练习题答案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Lua程序设计第四版第二部分编程实操自做练习题答案,带⭐为重点。

14.1 ⭐

该函数用于两个稀疏矩阵相加

function martixAdd(a, b)
    local c = {}
    for i = 1, #a, 1 do
        c[i] = {}
        for k, v in pairs(a[i]) do
            c[i][k] = v
        end
    end
    for i = 1, #b, 1 do
        for k, v in pairs(b[i]) do
            c[i][k] = (c[i][k] or 0) + v
            c[i][k] = (c[i][k] ~= 0) and c[i][k] or nil
        end
    end
    return c
end

A = {{
    [5] = 1
}, {}, {
    [1] = 3,
    [3] = 4
}, {}, {
    [4] = -1
}}

B = {{
    [2] = 2
}, {}, {
    [1] = -3,
    [4] = -3
}, {
    [3] = 8
}, {
    [1] = 7
}}

for k, v in pairs(martixAdd(A, B)) do
    for j, i in pairs(v) do
        print(k .. "行", j .. "列", i)
    end
end

14.2

改写队列的实现,使得当队列为空时两个索引都返回0

function listNew()
    return {
        _first = 0,
        _last = -1,
        first = function()
            if _first > _last then
                return 0
            end
            return _first
        end,
        last = function()
            if _first > _last then
                return 0
            end
            return _last
        end
    }
end

l = listNew()
-- 模拟空队列
l._first = 10
l._last = 9
print(l.first, l.last)

14.3

修改图所用的数据结构,使得图可以保存每条边的标签。该数据结构应该使用包括两个字段的对象来表示每一条边,即边的标签和边指向的节点。与邻接集合不同,每一个节点保存的是从当前节点出发的边的集合。

local function name2node(graph, name)
    local node = graph[name]
    if not node then
        graph[name] = {
            name = name,
            edges = {}
        }
        node = graph[name]
    end
    return node
end

function readgraph(file)
    local graph = {}
    f = io.open(file, 'r')
    for l in f:lines() do
        local name1, name2, edgeLabel = string.match(l, "(%S+)%s+(%S+)%s+(%S+)")
        local from = name2node(graph, name1)
        local to = name2node(graph, name2)
        table.insert(from.edges, {
            ["label"] = edgeLabel,
            ["adj"] = to
        })
    end
    return graph
end

graph = readgraph("graph.txt")
for k, v in pairs(graph) do
    for _, v in pairs(v.edges) do
        print(k, v.label, v.adj.name)
    end
end

[[
C       1       D
C       3       E
D       1       C
D       7       E
A       4       B
A       2       D
B       1       D
B       4       C
]]
A B 4
A D 2
B D 1
D C 1
C D 1
B C 4
D E 7
C E 3

14.4 ⭐

使用14.3的表示方式,其中边的标签代表两个终端节点之间的距离。该函数使用Dijkstra算法寻找两个指定节点之间的最短路径。

local function name2node(graph, name)
    local node = graph[name]
    if not node then
        graph[name] = {
            name = name,
            edges = {}
        }
        node = graph[name]
    end
    return node
end

function readgraph(file)
    local graph = {}
    f = io.open(file, 'r')
    for l in f:lines() do
        local name1, name2, edgeLabel = string.match(l, "(%S+)%s+(%S+)%s+(%S+)")
        local from = name2node(graph, name1)
        local to = name2node(graph, name2)
        table.insert(from.edges, {
            ["label"] = edgeLabel,
            ["adj"] = to
        })
    end
    return graph
end

graph = readgraph("graph.txt")
for k, v in pairs(graph) do
    for _, v in pairs(v.edges) do
        print(k, v.label, v.adj.name)
    end
end

function Dijkstra(graph, a, b)
    -- 点不存在
    if not graph[a] or not graph[b] then
        return nil
    end
    local D = {}
    local T = {}
    -- D 加入起点
    D[graph[a]] = 0
    -- T 加入其他点,初始化可达最短距离为正无穷大
    for name, node in pairs(graph) do
        if name ~= a then
            T[node] = math.maxinteger
        end
    end
    -- 当D中不包含b点时循环
    while not D[graph[b]] do
        -- 根据 D 迭代 T 可达最短距离
        for node, d in pairs(D) do
            for _, edge in pairs(node.edges) do
                if not D[edge.adj] then
                    local newD = d + tonumber(edge.label)
                    T[edge.adj] = math.min(T[edge.adj], newD)
                end
            end
        end
        -- 选择 T 可达距离最小的点
        local tmp = nil
        for node, d in pairs(T) do
            tmp = tmp or node
            if d < T[tmp] then
                tmp = node
            end
        end
        -- 如果没有点被选中,退出循环
        if T[tmp] == math.maxinteger then
            return nil
        end
        -- 将前面选择到的点加入D,并从T删除
        D[tmp] = T[tmp]
        T[tmp] = nil
    end
    return D[graph[b]]
end

d = Dijkstra(graph, "A", "E")
if not d then
    print("无路径")
end
print(d)

-- 具体路径计算为 A->E 路径距离 - (X->E 路径距离) == A -> X 路径距离
-- 即 A->X->E

15.1~15.2

修改序列化函数,使其带缩进地输出嵌套表,并添加["key"]=value的语法

function serialize(o, tab)
    tab = tab or 1
    local t = type(o)
    if t == "number" or t == "string" or t == "boolean" or t == "nil" then
        io.write(string.format("%q", o))
    elseif t == "table" then
        io.write("{\n")
        for k, v in pairs(o) do
            io.write(string.rep("   ", tab))
            io.write(" [", string.format("%s", serialize(k)), "] = ")
            serialize(v, tab + 1)
            io.write(",\n")
        end
        io.write(string.rep("   ", tab))
        io.write("}")
    else
        error("cannot serialize a" .. t)
    end
end

A = {
    B = {
        E = "dog",
        H = {
            F = "PIG",
            G = "goose"
        }
    },
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}}
}

serialize(A)

[[
{
    ["C"] = "apple",
    ["D"] = 114514,
    ["I"] = {
       [1] = 1,
       [2] = 2,
       [3] = 3,
       [4] = {
          [1] = 4,
          [2] = 5,
          [3] = 6,
         },
      },
    ["B"] = {
       ["E"] = "dog",
       ["H"] = {
          ["G"] = "goose",
          ["F"] = "PIG",
         },
      },
   }
]]

15.3

修改15.2,使其只在必要时,当键为字符串而不是合法标识符时才使用形如["key"]=value的语法

function serialize(o, tab)
    tab = tab or 1
    local t = type(o)
    if t == "number" or t == "string" or t == "boolean" or t == "nil" then
        io.write(string.format("%q", o))
    elseif t == "table" then
        io.write("{\n")
        for k, v in pairs(o) do
            io.write(string.rep("   ", tab))
            if type(k) == "string" and string.match(k, "[%a_]+[%w_]*") then
                io.write("  ", k, "  = ")
            else
                io.write(" [")
                serialize(k)
                io.write("] = ")
            end
            serialize(v, tab + 1)
            io.write(",\n")
        end
        io.write(string.rep("   ", tab))
        io.write("}")
    else
        error("cannot serialize a" .. t)
    end
end

A = {
    B = {
        E = "dog",
        H = {
            F = "PIG",
            G = "goose"
        }
    },
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}}
}

serialize(A)

[[
{
     C  = "apple",
     D  = 114514,
     I  = {
       [1] = 1,
       [2] = 2,
       [3] = 3,
       [4] = {
          [1] = 4,
          [2] = 5,
          [3] = 6,
         },
      },
     B  = {
        E  = "dog",
        H  = {
           G  = "goose",
           F  = "PIG",
         },
      },
   }
]]

15.4

使其在可能时使用列表的构造器语法。例如应将表{14,15,19}序列化为{14,15,19},而不是{[1] =14,[2]=15,[3]=19},在遍历该部分的时候不要重复序列化。

function serialize(o)
    local typ = type(o)
    if typ == "number" or typ == "string" or typ == "boolean" or typ == "nil" then
        io.write(string.format("%q", o))
    elseif typ == "table" then
        io.write("{\n")
        local hash = {}
        -- 遍历列表并进行hash记录
        for i, v in ipairs(o) do
            serialize(v)
            io.write(",")
            hash[i] = true
        end
        _ = (#hash ~= 0) and io.write("\n") or nil
        -- 遍历除列表之外的所有元素
        for k, v in pairs(o) do
            if type(k) == "string" and string.match(k, "[%a_]+[%w_]*") then
                io.write(" ", k, " = ")
            elseif hash[k] == true then
                goto continue
                -- do nothing
            else
                io.write(" [")
                serialize(k)
                io.write("] = ")
            end
            serialize(v)
            io.write(",\n")
            ::continue::
        end
        io.write("}")
    else
        error("cannot serialize a" .. t)
    end
end

A = {
    B = {
        E = "dog",
        H = {
            F = "PIG",
            G = "goose"
        }
    },
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}},
    [1] = 4,
    [2] = 3,
    [3] = 3,
    [5] = 4
}

serialize(A)

TEST = {
    4,
    3,
    3,
    [5] = 4,
    C = "apple",
    D = 114514,
    I = {1, 2, 3, {4, 5, 6}},
    B = {
        E = "dog",
        H = {
            G = "goose",
            F = "PIG"
        }
    }
}

[[
{
4,3,3,
 [5] = 4,
 C = "apple",
 D = 114514,
 I = {
1,2,3,{
4,5,6,
},
},
 B = {
 E = "dog",
 H = {
 G = "goose",
 F = "PIG",
},
},
}
]]

16.1

通常,在加载代码段时增加一些前缀很有用。该函数类似于函数load,不过会将第一个参数增加到待加载的代码段之前。

像原始的load函数一样,函数loadwithprefix应该既可以接收字符串形式的代码段,也可以通过函数进行读取。即使待加载的代码段是字符串形式的,函数loadwithprefix也不应该进行实际的字符串连接操作。相反,它应该调用函数load并传入一个恰当的读取函数来实现功能,这个读取函数首先返回要增加的代码,然后返回原始的代码段。

function loadWithPrefix(s, f)
    local tmp = 1
    return load(function()
        if tmp == 1 then
            tmp = 2
            return s
        elseif tmp == 2 then
            if type(f) == "string" then
                tmp = 3
                return f
            elseif type(f) == "function" then
                return f()
            end
        else
            return nil
        end
    end)
end

f = loadWithPrefix("local x = 100;", "print(x)")
f()

f = loadWithPrefix("local x = 100;", io.lines('load.txt', '*L'))
f()

16.2 ⭐

请编写一个函数mulitiload,该函数接收一组字符串或函数来生成函数。其他规则与16.1类似。

function multiLoad(...)
    local t = {...}
    local i = 1
    return load(function()
        ::continue::
        if i <= #t then
            local v = t[i]
            if type(v) == "string" then
                i = i + 1
                return v
            elseif type(v) == "function" then
                local tmp = v()
                if tmp then
                    return tmp
                else
                    i = i + 1
                    goto continue -- 进行下一回合
                end
            end
        else
            return nil
        end
    end)
end

f = multiLoad("local x = 100;", "print(x)", "x = 10; print(x);")
f()

f = multiLoad("local x = 100;", io.lines('load.txt', '*L'), "x = 10; print(x);")
f()

16.3 ⭐

该函数对于指定的n返回特定版本的函数stringrep_n。在实现方面不能使用闭包,而是应该构造出包含合理指令序列的Lua代码,然后再使用函数load生成最终的函数。请比较通用版本的stringrep函数与我们自己实现的版本之间的性能差异

自己实现的版本重复次数是固定的,省去了计算各组合次数的性能开销,速度更快。

function stringrepN(n)
    local top = [[local s = ...;local r = "";]]
    local middle = ""
    local tail = "r=r..s;return r;"
    local count = 0
    local tmp = n
    while n // 2 ~= 0 do
        count = count + 1
        n = n // 2
        middle = middle .. 's=s..s;'
    end
    for i = 1, tmp - 2 ^ count, 1 do
        top = top .. 'r=r..s;'
    end
    return load(top .. middle .. tail)
end

stringrep = stringrepN(256)

start = os.clock()
stringrep("abcdefg", 2 ^ 25)
print(os.clock() - start)

start = os.clock()
string.rep("abcdefg", 2 ^ 25)
print(os.clock() - start)

print(string.rep("a", 256) == stringrep("a"))

[[
0.0
0.321
true
]]

16.4

你能想到一个使pcall(pcall,f)的第1个返回值为false的f?

function b(x)
    print(x)
end

-- 一个函数和要传入的参数
ok, msg = pcall(b, 100)
print(ok, msg)
io.write("\n")
ok, msg = pcall(nil) -- 有错误信息但未引发错误
print(ok, msg)
io.write("\n")
ok, msg = pcall(pcall, nil) -- 错误信息返回了false
print(ok, msg)
io.write("\n")
ok, msg = pcall(pcall, (function()
end)()) -- 连nil也不返回的空value
print(ok, msg)
io.write("\n")

17.2

将几何区域系统的实现,9.4重写为恰当的模块

mod = require 'mod'
circle = mod.disk(0, 0, 0.5)
circle2 = mod.translate(circle, -0.3, 0.2)
shape = mod.difference(circle, circle2)
local plot = mod.plot
plot(shape, 512, 512, "test.pbm")
M = {}

function M.disk(cx, cy, r)
    return function(x, y)
        return (x - cx) ^ 2 + (y - cy) ^ 2 <= r ^ 2
    end
end

function M.rect(left, right, top, bottom)
    return function(x, y)
        return left <= x and x <= right and bottom <= y and y <= top
    end
end

function M.complement(r)
    return function(x, y)
        return not r(x, y)
    end
end

function M.union(r1, r2)
    return function(x, y)
        return r1(x, y) or r2(x, y)
    end
end

function M.intersection(r1, r2)
    return function(x, y)
        return r1(x, y) and r2(x, y)
    end
end

function M.difference(r1, r2)
    return function(x, y)
        return r1(x, y) and not r2(x, y)
    end
end

function M.translate(r, dx, dy)
    return function(x, y)
        return r(x - dx, y - dy)
    end
end

function M.plot(r, M, N, file)
    f = io.open(file, "w")
    f:write("P1\n", M, " ", N, "\n")
    for i = 1, N, 1 do
        local y = (N - i * 2) / N
        for j = 1, M do
            local x = (j * 2 - M) / M
            f:write(r(x, y) and "1" or "0")
        end
        f:write("\n")
    end
    f:close()
end

return M

17.3

如果在lua的搜索路径中添加一项绝对路径的文件名,会导致require一个不存在的模块时,这个模块都会搜索到固定文件名的文件作为它导入的模块
而且模块名不同,在package.loaded都会缓存,加载相同固定路径的module,在内存中也是两份独立的副本
在极端情况下有用,相当于始终会有默认的模块被加载,可以用这套机制来拓展require函数
https://www.lua.org/pil/8.1.html

17.4

编写一个同时搜索lua文件和C标准库的搜索器文章来源地址https://www.toymoban.com/news/detail-649505.html

function searcher(file, path)
    if type(file) ~= "string" or type(path) ~= "string" then
        return nil, error("not string type")
    end
    file = package.searchpath(file, path)
    if not file then
        return nil, error("no file")
    end
    f = loadfile(file)
    if not f then
        f = package.loadlib(file)
    end
    if not f then
        return nil, error("failed load")
    end
    return f(), nil
end

local m = searcher("mod", "./?.lua;./?.so;/usr/lib/lua5.2/?.so;/usr/share/lua5.2/?.lua")
print(m.rect(1, 1, 1, 1))

到了这里,关于《Lua程序设计第四版》 第二部分14~17章自做练习题答案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《Lua程序设计》--学习3

    Lua 文件 I/O | 菜鸟教程 (runoob.com) 对于文件操作来说,I/O库提供了两种不同的模型 。简单模型虚拟了一个当前输入流(current input stream)和一个当前输出流(current output stream),其I/O操作是通过这些流实现的。I/O库把当前输入流初始化为进程的标准输入(C语言中的stdin),将当

    2024年02月08日
    浏览(29)
  • 《Lua程序设计》--学习4

    在Lua语言中,函数是严格遵循词法定界(lexicalscoping)的 第一类值 (first-classvalue)。 “第一类值”意味着Lua语言中的函数与其他常见类型的值(例如数值和字符串)具有同等权限:一个程序可以将某个函数保存到变量中(全局变量和局部变量均可)或表中,也可以将某个函

    2024年02月08日
    浏览(75)
  • 《Lua程序设计》--学习2

    Lua语言中的 表 本质上是一种 辅助数组 (associative array),这种数组不仅可以使用数值作为索引,也可以使用字符串或其他任意类型的值作为索引(nil除外)。 当调用函数math.sin时,我们可能认为是“调用了math库中函数sin”;而对于Lua语言来说,其实际含义是“以字符串\\\"s

    2024年02月08日
    浏览(76)
  • Java Web程序设计课后习题答案 第四章

    第4章 一、填空 1.如果当前Web资源不想处理请求, RequestDispatcher接口提供了一个 forward()方法,该方法可以将当前请求传递给其他Web资源对这些信息进行处理并响应给客户端,这种方式称为请求转发。 2.Servlet API中,专门用来封装HTTP响应消息的接口是HttpServletResponse。 3. 重定向指

    2024年04月23日
    浏览(28)
  • 《汇编语言》王爽(第四版) 课程设计1

    文章目录 前言 一、课程设计任务 二、任务分析 1.公司数据的格式 2.数据转为字符串 3.显示多个数据 三、实现代码 总结 本文是王爽老师《汇编语言》(第四版) 课程设计1 “将实验七中给定的公司数据显示在屏幕上”的分析及代码。这是目前写的最综合的程序,要用到实验七

    2024年02月13日
    浏览(35)
  • 微信小程序毕业设计作品成品(17)微信小程序美食菜谱系统设计与实现

    博主介绍: 《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程,免费 项目配有对应开发文档、开题报告、任务书、PPT、论文模版

    2024年02月08日
    浏览(59)
  • 微信小程序毕业设计作品成品(14)微信小程序校园共享洗衣系统设计与实现

    博主介绍: 《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程,免费 项目配有对应开发文档、开题报告、任务书、PPT、论文模版

    2024年02月08日
    浏览(45)
  • 成都理工大学_Python程序设计_汇总(1-14单元)

    关于Python的缺点以下哪个描述是错误的? Python 2.x和Python 3.x兼容 ‍关于Python语言的应用,以下哪个描述是错误的? scikit-learn是Python自然语言工具包,用于诸如标记化、词形还原、词干化、解析、POS标注等任务。 ‎Jupyter Notebook (anaconda3)中的常用快捷键中,用于表示”分割单元

    2024年02月10日
    浏览(32)
  • 读程序员的README笔记14_技术设计流程(下)

    1.4.2.1. 该项目将需要至少一个月的工程时间 1.4.2.2. 变更将对软件的扩展和维护产生长期的影响 1.4.2.3. 变更将显著影响其他团队 1.5.2.1. 设计文档是一种工具,可以帮助你思考、获得反馈、让你的团队了解情况、培养新的工程师,并推动项目规划 1.5.5.1. 阅读大量的设计文档

    2024年02月04日
    浏览(36)
  • 《Verilog数字系统设计教程》夏宇闻 第四版思考题答案(第5章)

    1.为什么建议在编写Verilog模块程序时,如果用到 if 语句建议大家把配套的else情况也考虑在内?   因为如果没有配套的else语句,在不满足if条件语句时,将会保持原来的状态不变,从而在综合时会产生一个锁存器,而这是设计不想要的结果。 2.用 if(条件1) 语句;elseif (条件

    2024年02月08日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包