欢乐狗 发表于 2023-8-29 00:35:51

《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]
查看完整版本: 《Lua程序设计第四版》 第二部分14~17章自做练习题答案