《Lua程序设计第四版》 第二部分14~17章自做练习题答案
Lua程序设计第四版第二部分编程实操自做练习题答案,带⭐为重点。14.1 ⭐
该函数用于两个稀疏矩阵相加
function martixAdd(a, b)
local c = {}
for i = 1, #a, 1 do
c = {}
for k, v in pairs(a) do
c = v
end
end
for i = 1, #b, 1 do
for k, v in pairs(b) do
c = (c or 0) + v
c = (c ~= 0) and c or nil
end
end
return c
end
A = {{
= 1
}, {}, {
= 3,
= 4
}, {}, {
= -1
}}
B = {{
= 2
}, {}, {
= -3,
= -3
}, {
= 8
}, {
= 7
}}
for k, v in pairs(martixAdd(A, B)) do
for j, i in pairs(v) do
print(k .. "行", j .. "列", i)
end
end14.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
if not node then
graph = {
name = name,
edges = {}
}
node = graph
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 314.4 ⭐
使用14.3的表示方式,其中边的标签代表两个终端节点之间的距离。该函数使用Dijkstra算法寻找两个指定节点之间的最短路径。
https://xiaonenglife.oss-cn-hangzhou.aliyuncs.com/static/pic/2023/08/20230815163214_image-20230815163212132.pnglocal function name2node(graph, name)
local node = graph
if not node then
graph = {
name = name,
edges = {}
}
node = graph
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 or not graph then
return nil
end
local D = {}
local T = {}
-- D 加入起点
D] = 0
-- T 加入其他点,初始化可达最短距离为正无穷大
for name, node in pairs(graph) do
if name ~= a then
T = math.maxinteger
end
end
-- 当D中不包含b点时循环
while not D] do
-- 根据 D 迭代 T 可达最短距离
for node, d in pairs(D) do
for _, edge in pairs(node.edges) do
if not D then
local newD = d + tonumber(edge.label)
T = math.min(T, newD)
end
end
end
-- 选择 T 可达距离最小的点
local tmp = nil
for node, d in pairs(T) do
tmp = tmp or node
if d < T then
tmp = node
end
end
-- 如果没有点被选中,退出循环
if T == math.maxinteger then
return nil
end
-- 将前面选择到的点加入D,并从T删除
D = T
T = nil
end
return D]
end
d = Dijkstra(graph, "A", "E")
if not d then
print("无路径")
end
print(d)
-- 具体路径计算为 A->E 路径距离 - (X->E 路径距离) == A -> X 路径距离
-- 即 A->X->E15.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,
= 2,
= 3,
= {
= 4,
= 5,
= 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,
= 2,
= 3,
= {
= 4,
= 5,
= 6,
},
},
B= {
E= "dog",
H= {
G= "goose",
F= "PIG",
},
},
}
]]15.4
使其在可能时使用列表的构造器语法。例如应将表{14,15,19}序列化为{14,15,19},而不是{ =14,=15,=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 = 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 == 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}},
= 4,
= 3,
= 3,
= 4
}
serialize(A)
TEST = {
4,
3,
3,
= 4,
C = "apple",
D = 114514,
I = {1, 2, 3, {4, 5, 6}},
B = {
E = "dog",
H = {
G = "goose",
F = "PIG"
}
}
}
[[
{
4,3,3,
= 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
页:
[1]