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类似。
[code]function multiLoad(...) local t = {...} local i = 1 return load(function() ::continue:: if i |